Mujoco 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
7#include <gtest/gtest.h>
8
9#include <kdl/chainfksolverpos_recursive.hpp>
10
11#include <cmath>
12#include <filesystem>
13#include <memory>
14#include <string>
15
16namespace fs = std::filesystem;
17
18static constexpr double kHomePose[7] = { 0.0, 0.2618, 3.1416, -2.2689, 0.0, 0.9599, 1.5708 };
19
20static fs::path repo_root() { return fs::path(__FILE__).parent_path().parent_path(); }
21
22/* -------------------------------------------------------------------------
23 * Fixture 1: arm-only from scene.xml
24 * ------------------------------------------------------------------------- */
25
26class MjcfLoadTest : public testing::Test
27{
28 protected:
29 fs::path root_;
30 mjModel *model_ = nullptr;
31 mjData *data_ = nullptr;
33 unsigned n_ = 0;
34 std::unique_ptr<KDL::ChainFkSolverPos_recursive> fk_;
35 KDL::JntArray q_home_;
36
37 void SetUp() override
38 {
39 root_ = repo_root();
40 // scene.xml already has floor, lights, and skybox.
41 std::string mjcf = (root_ / "third_party/menagerie/kinova_gen3/scene.xml").string();
42 if (!fs::exists(mjcf)) {
43 GTEST_SKIP() << mjcf << " not found";
44 return;
45 }
46
48 sc.add_floor = false;
49 sc.add_skybox = false;
50 sc.robots.push_back(mj_kdl::RobotSpec{ .path = mjcf.c_str(), .attachments = {} });
51
52 ASSERT_TRUE(mj_kdl::build_scene(&model_, &data_, &sc));
53 ASSERT_EQ(model_->nv, 7);
54 ASSERT_GE(model_->nbody, 9);
55
56 ASSERT_TRUE(mj_kdl::init_robot_from_mjcf(&s_, model_, data_, "base_link", "bracelet_link"));
57 n_ = s_.chain.getNrOfJoints();
58 ASSERT_EQ(n_, 7u);
59
60 fk_ = std::make_unique<KDL::ChainFkSolverPos_recursive>(s_.chain);
61
62 // Use keyframe if available, else set manually.
63 int key_id = mj_name2id(model_, mjOBJ_KEY, "home");
64 if (key_id >= 0) {
65 mj_resetDataKeyframe(model_, data_, key_id);
66 } else {
67 KDL::JntArray q(n_);
68 for (unsigned i = 0; i < n_; ++i) q(i) = kHomePose[i];
70 }
71 mj_forward(model_, data_);
72
73 q_home_.resize(n_);
74 for (int i = 0; i < s_.n_joints; ++i) q_home_(i) = s_.data->qpos[s_.kdl_to_mj_qpos[i]];
75 }
76
77 void TearDown() override
78 {
79 if (model_) {
82 }
83 }
84};
85
86TEST_F(MjcfLoadTest, ModelLoaded)
87{
88 EXPECT_EQ(model_->nv, 7);
89 EXPECT_GE(model_->nbody, 9);
90}
91
93{
94 EXPECT_EQ(n_, 7u);
95}
96
97TEST_F(MjcfLoadTest, FKHomePose)
98{
99 KDL::Frame fk_home;
100 ASSERT_GE(fk_->JntToCart(q_home_, fk_home), 0) << "FK failed at home pose";
101 double dist = fk_home.p.Norm();
102 EXPECT_GE(dist, 0.1);
103 EXPECT_LE(dist, 1.1);
104}
105
106/* -------------------------------------------------------------------------
107 * Fixture 2: arm + Robotiq 2F-85 gripper from gen3.xml + 2f85.xml
108 * ------------------------------------------------------------------------- */
109
110class MjcfGripperTest : public testing::Test
111{
112 protected:
113 fs::path root_;
114 mjModel *model_ = nullptr;
115 mjData *data_ = nullptr;
117 unsigned n_ = 0;
118 std::unique_ptr<KDL::ChainFkSolverPos_recursive> fk_;
119
120 void SetUp() override
121 {
122 root_ = repo_root();
123 const std::string arm_mjcf =
124 (root_ / "third_party/menagerie/kinova_gen3/gen3.xml").string();
125 const std::string grp_mjcf =
126 (root_ / "third_party/menagerie/robotiq_2f85/2f85.xml").string();
127 if (!fs::exists(arm_mjcf)) {
128 GTEST_SKIP() << arm_mjcf << " not found";
129 return;
130 }
131 if (!fs::exists(grp_mjcf)) {
132 GTEST_SKIP() << grp_mjcf << " not found";
133 return;
134 }
135
137 .mjcf_path = grp_mjcf.c_str(),
138 .attach_to = "bracelet_link",
139 .prefix = "g_",
140 .pos = { 0.0, 0.0, -0.061525 },
141 .euler = { 180.0, 0.0, 0.0 },
142 .contact_exclusions = {},
143 };
145 rs.path = arm_mjcf.c_str();
146 rs.attachments.push_back(gs);
147
149 sc.robots.push_back(rs);
150
151 ASSERT_TRUE(mj_kdl::build_scene(&model_, &data_, &sc));
152 ASSERT_GE(model_->nq, 13);
153 ASSERT_GE(model_->nu, 8);
154
155 const mj_kdl::ToolFrameSpec tool{ .tool_body = "g_base", .tcp_site = "g_pinch" };
156 ASSERT_TRUE(
157 mj_kdl::init_robot_from_mjcf(&s_, model_, data_, "base_link", "bracelet_link", "", &tool)
158 );
159 n_ = s_.chain.getNrOfJoints();
160 ASSERT_EQ(n_, 7u);
161
162 fk_ = std::make_unique<KDL::ChainFkSolverPos_recursive>(s_.chain);
163
164 ASSERT_GE(mj_name2id(model_, mjOBJ_ACTUATOR, "g_fingers_actuator"), 0)
165 << "g_fingers_actuator not found";
166 }
167
168 void TearDown() override
169 {
170 if (model_) {
173 }
174 }
175};
176
178{
179 EXPECT_GE(model_->nq, 13);
180 EXPECT_GE(model_->nu, 8);
181}
182
184{
185 EXPECT_EQ(n_, 7u);
186}
187
189{
190 KDL::JntArray q_home(n_);
191 for (unsigned i = 0; i < n_; ++i) q_home(i) = kHomePose[i];
192 KDL::Frame fk_pose;
193 fk_->JntToCart(q_home, fk_pose);
194 double ee_dist = fk_pose.p.Norm();
195 EXPECT_GE(ee_dist, 0.1);
196 EXPECT_LE(ee_dist, 1.1);
197}
198
200{
201 int rdriver = mj_name2id(model_, mjOBJ_JOINT, "g_right_driver_joint");
202 ASSERT_GE(rdriver, 0) << "g_right_driver_joint not found";
203
204 double lo = model_->jnt_range[2 * rdriver];
205 double hi = model_->jnt_range[2 * rdriver + 1];
206 EXPECT_LE(std::abs(hi - 0.8), 0.01);
207 EXPECT_GE(lo, -0.01);
208}
209
210int main(int argc, char *argv[])
211{
212 testing::InitGoogleTest(&argc, argv);
213 return RUN_ALL_TESTS();
214}
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_
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)
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)