Mujoco KDL Wrapper  0.2.2
MuJoCo + KDL bridge for robot kinematics and dynamics
Loading...
Searching...
No Matches
ex_record.cpp
Go to the documentation of this file.
1/* ex_record.cpp (MJCF)
2 * Headless H.264 video recording via VideoRecorder (EGL + ffmpeg pipe).
3 *
4 * Records the Kinova GEN3 arm holding the home pose via gravity compensation
5 * for 5 s at 60 fps, loaded from MuJoCo Menagerie MJCF.
6 * No display or GLFW window is needed.
7 *
8 * Requires MuJoCo Menagerie in cache.
9 *
10 * Requirements:
11 * - BUILD_RECORDER=ON (default) -- EGL support compiled into the library
12 * - ffmpeg available in PATH -- sudo apt install ffmpeg
13 *
14 * Usage:
15 * ex_record [output.mp4] [360p|480p|720p|1080p|2k|4k]
16 *
17 * Output defaults to sim.mp4; resolution defaults to 1080p. */
18
20#include "example_paths.hpp"
21
22#include <kdl/chaindynparam.hpp>
23
24#include <cmath>
25#include <iostream>
26#include <string>
27
28static constexpr double kHomePose[7] = { 0.0, 0.2618, 3.1416, -2.2689, 0.0, 0.9599, 1.5708 };
29static constexpr int kFps = 60;
30static constexpr double kDuration = 5.0; // seconds
31
32static mj_kdl::VideoResolution parse_resolution(const std::string &s)
33{
34 if (s == "360p") return mj_kdl::VideoResolution::R360p;
35 if (s == "480p") return mj_kdl::VideoResolution::R480p;
36 if (s == "720p") return mj_kdl::VideoResolution::R720p;
37 if (s == "1080p") return mj_kdl::VideoResolution::R1080p;
38 if (s == "2k") return mj_kdl::VideoResolution::R2K;
39 if (s == "4k") return mj_kdl::VideoResolution::R4K;
40 std::cerr << "Unknown resolution '" << s << "'; using 1080p\n";
42}
43
44int main(int argc, char *argv[])
45{
46 std::string outmp4 = "sim.mp4";
48 for (int i = 1; i < argc; ++i) {
49 std::string a(argv[i]);
50 if (a.size() >= 4 && a.substr(a.size() - 4) == ".mp4")
51 outmp4 = a;
52 else
53 res = parse_resolution(a);
54 }
55
56 const std::string mjcf = mj_kdl_examples::menagerie_model("kinova_gen3/gen3.xml");
57
58 // Build scene
60 sc.timestep = 0.002;
61 sc.add_floor = true;
62 sc.add_skybox = true;
64 r.path = mjcf.c_str();
65 sc.robots.push_back(r);
66
67 mjModel *model = nullptr;
68 mjData *data = nullptr;
69 if (!mj_kdl::build_scene(&model, &data, &sc)) {
70 std::cerr << "build_scene() failed\n";
71 return 1;
72 }
73
74 mj_kdl::Robot robot;
75 if (!mj_kdl::init_robot_from_mjcf(&robot, model, data, "base_link", "bracelet_link")) {
76 std::cerr << "init_robot_from_mjcf() failed\n";
77 mj_kdl::destroy_scene(model, data);
78 return 1;
79 }
80
81 unsigned n = static_cast<unsigned>(robot.n_joints);
82 KDL::ChainDynParam dyn(robot.chain, KDL::Vector(0.0, 0.0, sc.gravity_z));
83
84 // Set home pose
85 KDL::JntArray q_home(n);
86 for (unsigned i = 0; i < n; ++i) q_home(i) = kHomePose[i];
87 mj_kdl::set_joint_pos(&robot, q_home);
89
90 // Prime gravity torques so first step gets correct compensation.
91 KDL::JntArray q(n), g(n);
92 dyn.JntToGravity(q_home, g);
93 for (unsigned i = 0; i < n; ++i) robot.jnt_trq_cmd[i] = g(i);
94
95 // Init video recorder
97 if (!mj_kdl::init_video_recorder(&vr, model, outmp4.c_str(), res, kFps)) {
98 std::cerr << "init_video_recorder() failed -- is EGL available and ffmpeg installed?\n";
99 mj_kdl::cleanup(&robot);
100 mj_kdl::destroy_scene(model, data);
101 return 1;
102 }
103
104 // Orbit camera slowly around the arm for a nicer recording.
105 vr.cam.elevation = -20.0;
106 vr.cam.distance = 1.8;
107 vr.cam.lookat[2] = 0.5;
108
109 const int total_steps = static_cast<int>(kDuration / model->opt.timestep);
110 const double step_per_deg = 360.0 / total_steps; // full orbit over kDuration
111 const int steps_per_frame = std::max(1, static_cast<int>(1.0 / (kFps * model->opt.timestep)));
112
113 std::cout << "Recording " << kDuration << " s to " << outmp4 << " (" << total_steps
114 << " steps, " << kFps << " fps)...\n";
115
116 for (int step = 0; step < total_steps; ++step) {
117 // Gravity compensation control
118 mj_kdl::update(&robot);
119 for (unsigned i = 0; i < n; ++i) q(i) = robot.jnt_pos_msr[i];
120 dyn.JntToGravity(q, g);
121 for (unsigned i = 0; i < n; ++i) robot.jnt_trq_cmd[i] = g(i);
122 mj_kdl::step(&robot);
123
124 // Record a frame at the target frame rate.
125 if (step % steps_per_frame == 0) {
126 vr.cam.azimuth = std::fmod(step * step_per_deg, 360.0);
127 if (!mj_kdl::record_frame(&vr, model, data)) {
128 std::cerr << "record_frame() failed at step " << step << "\n";
129 break;
130 }
131 }
132 }
133
134 mj_kdl::cleanup(&vr); // flushes pipe, finalises MP4
135
136 std::cout << "Saved: " << outmp4 << "\n";
137
138 mj_kdl::cleanup(&robot);
139 mj_kdl::destroy_scene(model, data);
140 return 0;
141}
int main(int argc, char *argv[])
Definition ex_record.cpp:44
bool init_video_recorder(VideoRecorder *vr, mjModel *model, const char *out_path, int width=1280, int height=720, int fps=60)
bool record_frame(VideoRecorder *vr, mjModel *model, mjData *data)
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 build_scene(mjModel **out_model, mjData **out_data, const SceneSpec *spec)
void destroy_scene(mjModel *model, mjData *data)
std::string menagerie_model(const fs::path &relative)
std::vector< double > jnt_pos_msr
std::vector< double > jnt_trq_cmd
std::vector< RobotSpec > robots