mj-kdl-wrapper  0.1.0
MuJoCo + KDL bridge for robot kinematics and dynamics
Loading...
Searching...
No Matches
test_mjcf_load.cpp
Go to the documentation of this file.
1/* test_mjcf_load.cpp
2 * Load Kinova GEN3 from MJCF and validate the KDL chain; also validates
3 * the combined arm + Robotiq 2F-85 model. */
4
6#include "test_utils.hpp"
7
8#include <gtest/gtest.h>
9
10#include <kdl/chainfksolverpos_recursive.hpp>
11
12#include <cmath>
13#include <filesystem>
14#include <iomanip>
15#include <iostream>
16#include <memory>
17#include <string>
18
19namespace fs = std::filesystem;
20
21static constexpr double kHomePose[7] = { 0.0, 0.2618, 3.1416, -2.2689, 0.0, 0.9599, 1.5708 };
22
23static fs::path repo_root() { return fs::path(__FILE__).parent_path().parent_path(); }
24
25/* -------------------------------------------------------------------------
26 * Fixture 1: arm-only from scene.xml
27 * ------------------------------------------------------------------------- */
28
29class MjcfLoadTest : public testing::Test
30{
31 protected:
32 fs::path root_;
33 mjModel *model_ = nullptr;
34 mjData *data_ = nullptr;
36 unsigned n_ = 0;
37 std::unique_ptr<KDL::ChainFkSolverPos_recursive> fk_;
38 KDL::JntArray q_home_;
39
40 void SetUp() override
41 {
42 root_ = repo_root();
43 if (!fs::exists(root_ / "third_party/menagerie")) {
44 GTEST_SKIP() << "third_party/menagerie/ not found";
45 return;
46 }
47
48 // scene.xml already has floor, lights, and skybox.
49 std::string mjcf = (root_ / "third_party/menagerie/kinova_gen3/scene.xml").string();
50
53 r.path = mjcf.c_str();
54 sc.add_floor = false;
55 sc.add_skybox = false;
56 sc.robots.push_back(r);
57
58 ASSERT_TRUE(mj_kdl::build_scene(&model_, &data_, &sc));
59 ASSERT_EQ(model_->nv, 7);
60 ASSERT_GE(model_->nbody, 9);
62 "model: nq=" << model_->nq << " nv=" << model_->nv << " nbody=" << model_->nbody
63 << " nu=" << model_->nu
64 );
65
66 ASSERT_TRUE(mj_kdl::init_robot_from_mjcf(&s_, model_, data_, "base_link", "bracelet_link"));
67 n_ = s_.chain.getNrOfJoints();
68 ASSERT_EQ(n_, 7u);
69
70 fk_ = std::make_unique<KDL::ChainFkSolverPos_recursive>(s_.chain);
71
72 // Use keyframe if available, else set manually.
73 int key_id = mj_name2id(model_, mjOBJ_KEY, "home");
74 if (key_id >= 0) {
75 mj_resetDataKeyframe(model_, data_, key_id);
76 } else {
77 KDL::JntArray q(n_);
78 for (unsigned i = 0; i < n_; ++i) q(i) = kHomePose[i];
80 }
81 mj_forward(model_, data_);
82
83 q_home_.resize(n_);
84 for (int i = 0; i < s_.n_joints; ++i) q_home_(i) = s_.data->qpos[s_.kdl_to_mj_qpos[i]];
85 }
86
87 void TearDown() override
88 {
89 if (model_) {
92 }
93 }
94};
95
96TEST_F(MjcfLoadTest, ModelLoaded)
97{
98 EXPECT_EQ(model_->nv, 7);
99 EXPECT_GE(model_->nbody, 9);
100}
101
103{
104 TEST_INFO("arm KDL chain: " << n_ << " joints");
105 EXPECT_EQ(n_, 7u);
106}
107
109{
110 KDL::Frame fk_home;
111 ASSERT_GE(fk_->JntToCart(q_home_, fk_home), 0) << "FK failed at home pose";
112 double dist = fk_home.p.Norm();
113 TEST_INFO(
114 "EE at home: [" << std::fixed << std::setprecision(4) << fk_home.p.x() << ", "
115 << fk_home.p.y() << ", " << fk_home.p.z() << "] dist=" << dist << " m"
116 );
117 EXPECT_GE(dist, 0.1);
118 EXPECT_LE(dist, 1.1);
119}
120
121/* -------------------------------------------------------------------------
122 * Fixture 2: arm + Robotiq 2F-85 gripper from gen3.xml + 2f85.xml
123 * ------------------------------------------------------------------------- */
124
125class MjcfGripperTest : public testing::Test
126{
127 protected:
128 fs::path root_;
129 mjModel *model_ = nullptr;
130 mjData *data_ = nullptr;
132 unsigned n_ = 0;
133 std::unique_ptr<KDL::ChainFkSolverPos_recursive> fk_;
134
135 void SetUp() override
136 {
137 root_ = repo_root();
138 if (!fs::exists(root_ / "third_party/menagerie")) {
139 GTEST_SKIP() << "third_party/menagerie/ not found";
140 return;
141 }
142
143 const std::string arm_mjcf =
144 (root_ / "third_party/menagerie/kinova_gen3/gen3.xml").string();
145 const std::string grp_mjcf =
146 (root_ / "third_party/menagerie/robotiq_2f85/2f85.xml").string();
147
149 gs.mjcf_path = grp_mjcf.c_str();
150 gs.attach_to = "bracelet_link";
151 gs.prefix = "g_";
152 gs.pos[2] = -0.061525;
153 gs.euler[0] = 180.0;
154
156 rs.path = arm_mjcf.c_str();
157 rs.attachments.push_back(gs);
158
160 sc.robots.push_back(rs);
161
162 ASSERT_TRUE(mj_kdl::build_scene(&model_, &data_, &sc));
163 TEST_INFO(
164 "model: nq=" << model_->nq << " nv=" << model_->nv << " nbody=" << model_->nbody
165 << " nu=" << model_->nu
166 );
167 ASSERT_GE(model_->nq, 13);
168 ASSERT_GE(model_->nu, 8);
169
170 ASSERT_TRUE(mj_kdl::init_robot_from_mjcf(&s_, model_, data_, "base_link", "bracelet_link"));
171 n_ = s_.chain.getNrOfJoints();
172 ASSERT_EQ(n_, 7u);
173
174 fk_ = std::make_unique<KDL::ChainFkSolverPos_recursive>(s_.chain);
175
176 ASSERT_GE(mj_name2id(model_, mjOBJ_ACTUATOR, "g_fingers_actuator"), 0)
177 << "g_fingers_actuator not found";
178 }
179
180 void TearDown() override
181 {
182 if (model_) {
185 }
186 }
187};
188
190{
191 EXPECT_GE(model_->nq, 13);
192 EXPECT_GE(model_->nu, 8);
193}
194
196{
197 TEST_INFO("arm KDL chain: " << n_ << " joints");
198 EXPECT_EQ(n_, 7u);
199}
200
202{
203 KDL::JntArray q_home(n_);
204 for (unsigned i = 0; i < n_; ++i) q_home(i) = kHomePose[i];
205 KDL::Frame fk_pose;
206 fk_->JntToCart(q_home, fk_pose);
207 double ee_dist = fk_pose.p.Norm();
208 TEST_INFO(
209 "EE distance at home: " << std::fixed << std::setprecision(3) << ee_dist * 1000.0 << " mm"
210 );
211 EXPECT_GE(ee_dist, 0.1);
212 EXPECT_LE(ee_dist, 1.1);
213}
214
216{
217 int rdriver = mj_name2id(model_, mjOBJ_JOINT, "g_right_driver_joint");
218 ASSERT_GE(rdriver, 0) << "g_right_driver_joint not found";
219
220 double lo = model_->jnt_range[2 * rdriver];
221 double hi = model_->jnt_range[2 * rdriver + 1];
222 TEST_INFO(
223 "gripper right_driver_joint range: [" << std::fixed << std::setprecision(4) << lo << ", "
224 << hi << "] rad"
225 );
226 EXPECT_LE(std::abs(hi - 0.8), 0.01);
227 EXPECT_GE(lo, -0.01);
228}
229
230int main(int argc, char *argv[])
231{
232 testing::InitGoogleTest(&argc, argv);
233 return RUN_ALL_TESTS();
234}
void SetUp() override
std::unique_ptr< KDL::ChainFkSolverPos_recursive > fk_
void TearDown() override
std::unique_ptr< KDL::ChainFkSolverPos_recursive > fk_
KDL::JntArray q_home_
void TearDown() override
void SetUp() override
mj_kdl::Robot s_
bool init_robot_from_mjcf(Robot *r, mjModel *model, mjData *data, const char *base_body, const char *tip_body, const char *prefix="", const char *tool_body=nullptr)
void cleanup(Robot *r)
void set_joint_pos(Robot *r, const KDL::JntArray &q, bool call_forward=true)
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< int > kdl_to_mj_qpos
std::vector< RobotSpec > robots
int main(int argc, char *argv[])
TEST_F(MjcfLoadTest, ModelLoaded)
#define TEST_INFO(msg)