Mujoco KDL Wrapper  0.2.2
MuJoCo + KDL bridge for robot kinematics and dynamics
Loading...
Searching...
No Matches
ex_table_scene.cpp
Go to the documentation of this file.
1/* ex_table_scene.cpp (MJCF)
2 * Kinova GEN3 + Robotiq 2F-85 gripper on a table with a few free objects,
3 * loaded from MuJoCo Menagerie MJCF.
4 *
5 * The arm runs KDL gravity compensation; the gripper cycles open/closed every 3 s.
6 *
7 * Requires MuJoCo Menagerie in cache.
8 *
9 * Usage:
10 * ex_table_scene_mjcf [--headless]
11 *
12 * With --headless runs 500 steps and prints final EE drift. */
13
15#include "example_paths.hpp"
16
17#include <kdl/chaindynparam.hpp>
18#include <kdl/chainfksolverpos_recursive.hpp>
19
20#include <cmath>
21#include <iomanip>
22#include <iostream>
23#include <string>
24
25static constexpr double kHomePose[7] = { 0.0, 0.2618, 3.1416, -2.2689, 0.0, 0.9599, 1.5708 };
26
27static mj_kdl::SceneObject make_box(
28 const char *name,
29 double x,
30 double y,
31 double hx,
32 double hy,
33 double hz,
34 float r,
35 float g,
36 float b,
37 double surface_z
38)
39{
41 o.name = name;
43 o.size[0] = hx;
44 o.size[1] = hy;
45 o.size[2] = hz;
46 o.pos[0] = x;
47 o.pos[1] = y;
48 o.pos[2] = surface_z + hz;
49 o.rgba[0] = r;
50 o.rgba[1] = g;
51 o.rgba[2] = b;
52 o.rgba[3] = 1.0f;
53 return o;
54}
55
56static mj_kdl::SceneObject make_sphere(
57 const char *name,
58 double x,
59 double y,
60 double radius,
61 float r,
62 float g,
63 float b,
64 double surface_z
65)
66{
68 o.name = name;
70 o.size[0] = radius;
71 o.pos[0] = x;
72 o.pos[1] = y;
73 o.pos[2] = surface_z + radius;
74 o.rgba[0] = r;
75 o.rgba[1] = g;
76 o.rgba[2] = b;
77 o.rgba[3] = 1.0f;
78 return o;
79}
80
81int main(int argc, char *argv[])
82{
83 bool headless = false;
84 for (int i = 1; i < argc; ++i)
85 if (std::string(argv[i]) == "--headless") headless = true;
86
87 const std::string mjcf = mj_kdl_examples::menagerie_model("kinova_gen3/gen3.xml");
88 const std::string grp_mjcf = mj_kdl_examples::asset("robotiq_2f85/2f85.xml");
89 const std::string table_mjcf = mj_kdl_examples::asset("table.xml");
90
92 sc.timestep = 0.002;
93 sc.add_floor = true;
94 sc.add_skybox = true;
95 const double surface_z = 0.7;
96
98 gs.mjcf_path = grp_mjcf.c_str();
99 gs.attach_to = { mj_kdl::AttachKind::Site, "pinch_site" };
100 gs.prefix = "g_";
101
103 r.path = mjcf.c_str();
104 r.pos[2] = surface_z;
105 r.attachments.push_back(gs);
106 sc.robots.push_back(r);
107
109 .name = "table",
110 .mjcf_path = table_mjcf,
111 .pos = { 0.0, 0.0, surface_z },
112 .fixed = true,
113 };
114 sc.objects.push_back(table);
115 sc.objects.push_back(
116 make_box("red_cube", 0.35, 0.10, 0.03, 0.03, 0.03, 1.0f, 0.2f, 0.2f, surface_z)
117 );
118 sc.objects.push_back(
119 make_box("green_cube", 0.35, -0.10, 0.03, 0.03, 0.03, 0.2f, 1.0f, 0.2f, surface_z)
120 );
121 sc.objects.push_back(
122 make_box("blue_cube", 0.35, 0.30, 0.04, 0.04, 0.04, 0.2f, 0.2f, 1.0f, surface_z)
123 );
124 sc.objects.push_back(
125 make_sphere("orange_sphere", -0.20, 0.20, 0.035, 1.0f, 0.55f, 0.0f, surface_z)
126 );
127 sc.objects.push_back(
128 make_sphere("purple_sphere", -0.20, -0.20, 0.025, 0.7f, 0.0f, 0.9f, surface_z)
129 );
130
131 /* Static scene cameras. The Kinova MJCF also contributes a "wrist" camera;
132 * all of them are enumerated by get_camera_names() after build_scene(). */
133 sc.cameras.push_back(mj_kdl::CameraSpec{
134 .name = "overview",
135 .pos = { 0.0, -0.6, 1.6 }, // in front of and above the table
136 .euler = { 34.0, 0.0, 0.0 }, // tilt down toward table
137 .fovy = 55.0,
138 });
139 sc.cameras.push_back(mj_kdl::CameraSpec{
140 .name = "side",
141 .pos = { -1.0, 0.0, 1.1 }, // left side, arm height
142 .euler = { 0.0, -68.0, 0.0 }, // pitch down toward robot base
143 .fovy = 50.0,
144 });
145
146 mjModel *model = nullptr;
147 mjData *data = nullptr;
148 mj_kdl::Robot robot;
149 if (!mj_kdl::build_scene(&model, &data, &sc)) {
150 std::cerr << "build_scene() failed\n";
151 return 1;
152 }
153
154 KDL::Frame world_T_table_top;
155 const std::string table_top_site = mj_kdl::scene_object_site_name(table, "table_top");
156 if (!mj_kdl::get_site_frame(model, data, table_top_site.c_str(), &world_T_table_top)) {
157 std::cerr << "table_top site not found\n";
158 mj_kdl::destroy_scene(model, data);
159 return 1;
160 }
161 std::cout << "table top z = " << world_T_table_top.p.z() << "\n";
162
163 std::cout << "cameras:";
164 for (const auto &name : mj_kdl::get_camera_names(model))
165 std::cout << " " << name;
166 std::cout << "\n";
167
168 const mj_kdl::ToolFrameSpec tool{ .tool_body = "g_base", .tcp_site = "g_pinch" };
169
171 &robot, model, data, "base_link", "bracelet_link", "", &tool
172 )) {
173 std::cerr << "init_robot_from_mjcf() failed\n";
174 mj_kdl::destroy_scene(model, data);
175 return 1;
176 }
177
178 unsigned n = static_cast<unsigned>(robot.n_joints);
179 KDL::ChainFkSolverPos_recursive fk(robot.chain);
180 KDL::ChainDynParam dyn(robot.chain, KDL::Vector(0.0, 0.0, sc.gravity_z));
181
182 KDL::JntArray q_home(n);
183 for (unsigned i = 0; i < n; ++i) q_home(i) = kHomePose[i];
184
185 int fingers_act = mj_name2id(model, mjOBJ_ACTUATOR, "g_fingers_actuator");
186
188
189 mj_kdl::Env env;
190 env.spec = sc;
191 env.model = model;
192 env.data = data;
193 mj_kdl::env_add_robot(&env, &robot);
194
195 env.on_reset = [&](mj_kdl::ResetContext *) {
196 mj_kdl::set_joint_pos(&robot, q_home, false);
197 if (fingers_act >= 0) data->ctrl[fingers_act] = 0.8;
198 };
199
200 mj_kdl::reset(&env);
201
202 KDL::JntArray q(n), g(n);
203 auto ctrl_step = [&]() {
204 mj_kdl::update(&robot);
205 for (unsigned i = 0; i < n; ++i) q(i) = robot.jnt_pos_msr[i];
206 dyn.JntToGravity(q, g);
207 for (unsigned i = 0; i < n; ++i) robot.jnt_trq_cmd[i] = g(i);
208 if (fingers_act >= 0)
209 data->ctrl[fingers_act] = (std::fmod(data->time, 6.0) < 3.0) ? 0.8 : 0.0;
210 };
211
212 if (headless) {
213 KDL::Frame ee_start;
214 fk.JntToCart(q_home, ee_start);
215
216 for (int step = 0; step < 500; ++step) {
217 ctrl_step();
218 mj_kdl::step(&robot);
219 }
220
221 KDL::JntArray q_end(n);
222 for (unsigned i = 0; i < n; ++i) q_end(i) = robot.jnt_pos_msr[i];
223 KDL::Frame ee_end;
224 fk.JntToCart(q_end, ee_end);
225 double drift = (ee_start.p - ee_end.p).Norm();
226 std::cout << "EE drift after 500 steps: " << std::fixed << std::setprecision(3)
227 << drift * 1000.0 << " mm\n";
228 } else {
229 mj_kdl::Viewer viewer;
230 if (!mj_kdl::init_window_sim(&viewer, &robot)) {
231 std::cerr << "init_window_sim() failed\n";
232 mj_kdl::cleanup(&robot);
233 mj_kdl::destroy_scene(model, data);
234 return 1;
235 }
236
237 double prev_sim_time = data->time;
238 while (true) {
239 if (data->time < prev_sim_time - 1e-6) mj_kdl::reset(&env);
240 prev_sim_time = data->time;
241 ctrl_step();
242 if (!mj_kdl::step(&robot)) break;
243 }
244
245 mj_kdl::cleanup(&viewer);
246 }
247
248 mj_kdl::cleanup(&robot);
249 mj_kdl::destroy_scene(model, data);
250 return 0;
251}
int main(int argc, char *argv[])
void env_add_robot(Env *env, Robot *robot)
ResetInfo reset(Env *env, const ResetOptions *options=nullptr)
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 build_scene(mjModel **out_model, mjData **out_data, const SceneSpec *spec)
void destroy_scene(mjModel *model, mjData *data)
std::vector< std::string > get_camera_names(const mjModel *model)
std::string scene_object_site_name(const SceneObject &obj, const char *site_name)
bool init_window_sim(Viewer *v, Robot *r, const char *title="MuJoCo")
std::string asset(const fs::path &relative)
std::string menagerie_model(const fs::path &relative)
ResetHook on_reset
std::vector< AttachmentSpec > attachments
std::vector< double > jnt_pos_msr
std::vector< double > jnt_trq_cmd
std::vector< RobotSpec > robots
std::vector< SceneObject > objects
std::vector< CameraSpec > cameras