Mujoco KDL Wrapper  0.2.2
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 "example_paths.hpp"
7
8#include <gtest/gtest.h>
9
10#include <kdl/chainfksolverpos_recursive.hpp>
11
12#include <cmath>
13#include <filesystem>
14#include <memory>
15#include <string>
16
17namespace fs = std::filesystem;
18
19static constexpr double kHomePose[7] = { 0.0, 0.2618, 3.1416, -2.2689, 0.0, 0.9599, 1.5708 };
20
21/* -------------------------------------------------------------------------
22 * Fixture 1: arm-only from scene.xml
23 * ------------------------------------------------------------------------- */
24
25class MjcfLoadTest : public testing::Test
26{
27 protected:
28 fs::path root_;
29 mjModel *model_ = nullptr;
30 mjData *data_ = nullptr;
32 unsigned n_ = 0;
33 std::unique_ptr<KDL::ChainFkSolverPos_recursive> fk_;
34 KDL::JntArray q_home_;
35
36 void SetUp() override
37 {
38 // scene.xml already has floor, lights, and skybox.
39 std::string mjcf = mj_kdl_examples::find_menagerie_model("kinova_gen3/scene.xml");
40 if (!fs::exists(mjcf)) {
41 GTEST_SKIP() << mjcf << " not found";
42 return;
43 }
44
46 sc.timestep = 0.002;
47 sc.add_floor = true;
48 sc.add_skybox = true;
49 sc.add_floor = false;
50 sc.add_skybox = false;
51 sc.robots.push_back(mj_kdl::RobotSpec{ .path = mjcf.c_str(), .attachments = {} });
52
53 ASSERT_TRUE(mj_kdl::build_scene(&model_, &data_, &sc));
54 ASSERT_EQ(model_->nv, 7);
55 ASSERT_GE(model_->nbody, 9);
56
57 ASSERT_TRUE(mj_kdl::init_robot_from_mjcf(&s_, model_, data_, "base_link", "bracelet_link"));
58 n_ = s_.chain.getNrOfJoints();
59 ASSERT_EQ(n_, 7u);
60
61 fk_ = std::make_unique<KDL::ChainFkSolverPos_recursive>(s_.chain);
62
63 // Use keyframe if available, else set manually.
64 int key_id = mj_name2id(model_, mjOBJ_KEY, "home");
65 if (key_id >= 0) {
66 mj_resetDataKeyframe(model_, data_, key_id);
67 } else {
68 KDL::JntArray q(n_);
69 for (unsigned i = 0; i < n_; ++i) q(i) = kHomePose[i];
71 }
72 mj_forward(model_, data_);
73
74 q_home_.resize(n_);
75 for (int i = 0; i < s_.n_joints; ++i) q_home_(i) = s_.data->qpos[s_.kdl_to_mj_qpos[i]];
76 }
77
78 void TearDown() override
79 {
80 if (model_) {
83 }
84 }
85};
86
87TEST_F(MjcfLoadTest, ModelLoaded)
88{
89 EXPECT_EQ(model_->nv, 7);
90 EXPECT_GE(model_->nbody, 9);
91}
92
94{
95 EXPECT_EQ(n_, 7u);
96}
97
98TEST_F(MjcfLoadTest, FKHomePose)
99{
100 KDL::Frame fk_home;
101 ASSERT_GE(fk_->JntToCart(q_home_, fk_home), 0) << "FK failed at home pose";
102 double dist = fk_home.p.Norm();
103 EXPECT_GE(dist, 0.1);
104 EXPECT_LE(dist, 1.1);
105}
106
107/* -------------------------------------------------------------------------
108 * Fixture 2: arm + Robotiq 2F-85 gripper from gen3.xml + 2f85.xml
109 * ------------------------------------------------------------------------- */
110
111class MjcfGripperTest : public testing::Test
112{
113 protected:
114 fs::path root_;
115 mjModel *model_ = nullptr;
116 mjData *data_ = nullptr;
118 unsigned n_ = 0;
119 std::unique_ptr<KDL::ChainFkSolverPos_recursive> fk_;
120
121 void SetUp() override
122 {
123 const std::string arm_mjcf =
124 mj_kdl_examples::find_menagerie_model("kinova_gen3/gen3.xml");
125 const std::string grp_mjcf =
126 mj_kdl_examples::find_asset("robotiq_2f85/2f85.xml");
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 = { mj_kdl::AttachKind::Site, "pinch_site" },
139 .prefix = "g_",
140 .contact_exclusions = {},
141 };
143 rs.path = arm_mjcf.c_str();
144 rs.attachments.push_back(gs);
145
147 sc.timestep = 0.002;
148 sc.add_floor = true;
149 sc.add_skybox = true;
150 sc.robots.push_back(rs);
151
152 ASSERT_TRUE(mj_kdl::build_scene(&model_, &data_, &sc));
153 ASSERT_GE(model_->nq, 13);
154 ASSERT_GE(model_->nu, 8);
155
156 const mj_kdl::ToolFrameSpec tool{ .tool_body = "g_base", .tcp_site = "g_pinch" };
157 ASSERT_TRUE(
158 mj_kdl::init_robot_from_mjcf(&s_, model_, data_, "base_link", "bracelet_link", "", &tool)
159 );
160 n_ = s_.chain.getNrOfJoints();
161 ASSERT_EQ(n_, 7u);
162
163 fk_ = std::make_unique<KDL::ChainFkSolverPos_recursive>(s_.chain);
164
165 ASSERT_GE(mj_name2id(model_, mjOBJ_ACTUATOR, "g_fingers_actuator"), 0)
166 << "g_fingers_actuator not found";
167 }
168
169 void TearDown() override
170 {
171 if (model_) {
174 }
175 }
176};
177
179{
180 EXPECT_GE(model_->nq, 13);
181 EXPECT_GE(model_->nu, 8);
182}
183
185{
186 EXPECT_EQ(n_, 7u);
187}
188
190{
191 KDL::JntArray q_home(n_);
192 for (unsigned i = 0; i < n_; ++i) q_home(i) = kHomePose[i];
193 KDL::Frame fk_pose;
194 fk_->JntToCart(q_home, fk_pose);
195 double ee_dist = fk_pose.p.Norm();
196 EXPECT_GE(ee_dist, 0.1);
197 EXPECT_LE(ee_dist, 1.1);
198}
199
201{
202 int rdriver = mj_name2id(model_, mjOBJ_JOINT, "g_right_driver_joint");
203 ASSERT_GE(rdriver, 0) << "g_right_driver_joint not found";
204
205 double lo = model_->jnt_range[2 * rdriver];
206 double hi = model_->jnt_range[2 * rdriver + 1];
207 EXPECT_LE(std::abs(hi - 0.8), 0.01);
208 EXPECT_GE(lo, -0.01);
209}
210
211TEST_F(MjcfGripperTest, JointPositionByName)
212{
213 int rdriver = mj_name2id(model_, mjOBJ_JOINT, "g_right_driver_joint");
214 ASSERT_GE(rdriver, 0) << "g_right_driver_joint not found";
215
216 int ldriver = mj_name2id(model_, mjOBJ_JOINT, "g_left_driver_joint");
217 ASSERT_GE(ldriver, 0) << "g_left_driver_joint not found";
218 data_->qpos[model_->jnt_qposadr[rdriver]] = 0.42;
219 data_->qpos[model_->jnt_qposadr[ldriver]] = 0.42;
220
221 double measured = 0.0;
222 ASSERT_TRUE(mj_kdl::get_joint_position(model_, data_, "g_right_driver_joint", &measured));
223 EXPECT_NEAR(measured, 0.42, 1e-9);
224
225 // An actuator name resolves to its transmission joint's qpos.
226 measured = 0.0;
227 ASSERT_TRUE(mj_kdl::get_joint_position(model_, data_, "g_fingers_actuator", &measured));
228 EXPECT_NEAR(measured, 0.42, 1e-9);
229
230 EXPECT_FALSE(mj_kdl::get_joint_position(model_, data_, "no_such_joint", &measured));
231}
232
233int main(int argc, char *argv[])
234{
235 testing::InitGoogleTest(&argc, argv);
236 return RUN_ALL_TESTS();
237}
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)
bool get_joint_position(const mjModel *model, mjData *data, const char *name, double *out)
void destroy_scene(mjModel *model, mjData *data)
std::string find_asset(const fs::path &relative)
std::string find_menagerie_model(const fs::path &relative)
std::vector< AttachmentSpec > attachments
std::vector< RobotSpec > robots
int main(int argc, char *argv[])
TEST_F(MjcfLoadTest, ModelLoaded)