Mujoco KDL Wrapper  0.3.18
MuJoCo + KDL bridge for robot kinematics and dynamics
Loading...
Searching...
No Matches
mj_kdl_wrapper.hpp
Go to the documentation of this file.
1/* SPDX-License-Identifier: MIT
2 * Copyright (c) 2026 Vamsi Kalagaturu
3 * See LICENSE for details. */
4
5#pragma once
6
7#include <mujoco/mujoco.h>
8#include <GLFW/glfw3.h>
9#include <kdl/chain.hpp>
10#include <kdl/frames.hpp>
11#include <kdl/jntarray.hpp>
12#include <cstdint>
13#include <cstdio>
14#include <cstring>
15#include <functional>
16#include <sstream>
17#include <string>
18#include <vector>
19#include <chrono>
20
21namespace mj_kdl {
22
23/**
24 * @ingroup grp_logging
25 * Log verbosity level. Each level includes all levels below it:
26 * NONE - nothing printed.
27 * INFO - informational messages only (scene/chain construction progress).
28 * WARN - INFO + recoverable warnings (e.g. fallback to headless mode).
29 * ERROR - all messages, including errors that cause functions to fail. Default.
30 */
31enum class LogLevel { NONE = 0, INFO = 1, WARN = 2, ERROR = 3 };
32
33/** @ingroup grp_logging
34 * Library-wide log verbosity (inline so one shared instance across all TUs). */
36
37/** @ingroup grp_logging
38 * Set the library-wide log verbosity. */
39inline void set_log_level(LogLevel level) { g_log_level = level; }
40/** @ingroup grp_logging
41 * Get the library-wide log verbosity. */
43
44} // namespace mj_kdl
45
46/* @ingroup grp_logging
47 * Internal logging macros, exposed so wrapper users (examples, tests, and
48 * downstream code) can emit messages through the same stream/level filter.
49 * MJ_LOG_ is the primitive; LOG_INFO/LOG_WARN/LOG_ERROR are the entry points.
50 * `expr` may use << to build the message: LOG_INFO("count=" << n).
51 *
52 * Defined at file scope (not inside the mj_kdl namespace) because macros are
53 * not namespaced; the MJ_ prefix avoids collisions.
54 */
55#define MJ_FILENAME_ (::strrchr(__FILE__, '/') ? ::strrchr(__FILE__, '/') + 1 : __FILE__)
56
57#define MJ_LOG_(lvl_enum, color, label, expr) \
58 do { \
59 if (::mj_kdl::g_log_level >= ::mj_kdl::LogLevel::lvl_enum) { \
60 std::ostringstream _mj_oss; \
61 _mj_oss << expr; /* NOLINT(bugprone-macro-parentheses) */ \
62 std::fprintf( \
63 stderr, \
64 color "[mj_kdl " label "] %s:%d (%s): %s\033[0m\n", \
65 MJ_FILENAME_, \
66 __LINE__, \
67 __func__, \
68 _mj_oss.str().c_str() \
69 ); \
70 } \
71 } while (0)
72
73#define LOG_INFO(expr) MJ_LOG_(INFO, "", "INFO ", expr)
74#define LOG_WARN(expr) MJ_LOG_(WARN, "\033[33m", "WARN ", expr)
75#define LOG_ERROR(expr) MJ_LOG_(ERROR, "\033[31m", "ERROR", expr)
76
77namespace mj_kdl {
78
79/**
80 * @ingroup grp_types
81 * Kind of element an AttachTarget references in the accumulated scene spec.
82 * World selects the worldbody (the default); Body, Site, and Frame each look
83 * up a named element of the corresponding type.
84 */
85enum class AttachKind { World, Body, Site, Frame };
86
87/**
88 * @ingroup grp_types
89 * Where to attach a robot, object, or attachment in the accumulated scene spec.
90 * Tagged so exactly one alternative is encoded; defaulting to World keeps
91 * callers that omit attach_to anchored to the worldbody.
92 * For Site, the site's own pos/quat becomes the placement frame and the
93 * accompanying pos/quat are an additional offset on top of it (matches MJCF).
94 */
96{
98 const char *name = nullptr; // ignored when kind == World
99};
100
101/**
102 * @ingroup grp_types
103 * One link in an ordered attachment chain for a robot.
104 * An attachment is any MJCF body (end effector, mount, FT sensor, tool, additional arm
105 * on a mobile base, etc.) attached under a named element in the accumulated robot spec.
106 * Attachments are applied in declaration order; attach_to may reference any body, site,
107 * or frame present after all prior attachments have been applied.
108 */
110{
111 const char *mjcf_path = nullptr; // MJCF file for this attachment
112 AttachTarget attach_to; // parent in root or prior attachment (default: world)
113 const char *prefix = ""; // element name prefix (avoids name conflicts)
114 double pos[3] = { 0, 0, 0 }; // position offset [m]
115 double quat[4] = { 0, 0, 0, 1 }; // orientation offset [x, y, z, w]
116
117 /* Contact exclusion pairs registered by attach_to_spec(). */
118 std::vector<std::pair<std::string, std::string>> contact_exclusions; // (body1, body2) pairs
119};
120
121/**
122 * @ingroup grp_types
123 * One robot in a scene: a root MJCF (arm, mobile base, ...) with an ordered attachment
124 * chain and a placement target.
125 *
126 * attachments is applied in order by build_scene() / attach_to_spec(): each entry's
127 * attach_to may reference any body, site, or frame in the accumulated spec (root + all
128 * prior attachments). This naturally supports: fixed arm, arm+gripper, arm+mount+FT+
129 * gripper, mobile base, mobile manipulator (base root, arm as first attachment), etc.
130 *
131 * attach_to selects where the robot root is placed in the scene; the default is the
132 * worldbody. Set it to e.g. { AttachKind::Site, "table_mount" } to place the robot on
133 * a tabletop site exported by a prior scene object. pos/quat are offsets in the
134 * resolved parent frame.
135 *
136 * path is the root MJCF passed to build_scene(). prefix must be unique per robot
137 * in multi-robot scenes.
138 */
140{
141 const char *path = nullptr; // root MJCF path
142 const char *prefix = ""; // element name prefix
143 AttachTarget attach_to; // placement parent (default: world)
144 double pos[3] = { 0, 0, 0 }; // offset in parent frame [m]
145 double quat[4] = { 0, 0, 0, 1 }; // orientation offset [x, y, z, w]
146 std::vector<AttachmentSpec> attachments; // ordered attachment chain; empty = none
147};
148
149/** @ingroup grp_types
150 * Shape type for scene objects. Unspecified is the sentinel value;
151 * build_scene rejects a primitive SceneObject whose shape is Unspecified. */
153
154/**
155 * @ingroup grp_types
156 * Contact-friction dimensionality, matching MuJoCo's `condim` integer values.
157 * Tangential (3) - sliding friction only (default).
158 * Torsional (4) - +torsion about the contact normal.
159 * Rolling (6) - +torsion and +rolling resistance.
160 * Values 1 (frictionless) and 2 (1D friction) exist in MuJoCo but are
161 * uncommon; if needed, pass `static_cast<Condim>(1)` etc.
162 */
163enum class Condim : int { Tangential = 3, Torsional = 4, Rolling = 6 };
164
165/**
166 * @ingroup grp_types
167 * A free-floating or fixed rigid body to place in the scene.
168 *
169 * size:
170 * BOX - half-extents (x, y, z)
171 * SPHERE - {radius, 0, 0}
172 * CYLINDER - {radius, half-length, 0}
173 * Ignored when mjcf_path is set.
174 *
175 * attach_to:
176 * Parent in the accumulated scene spec. Default is the worldbody. A child
177 * object must appear after its parent in SceneSpec::objects.
178 * MuJoCo constraint: a body that carries a freejoint must be a direct child
179 * of the worldbody. So a non-fixed primitive (fixed == false) and any
180 * mjcf_path asset whose root body owns a freejoint must use AttachKind::World.
181 * Fixed primitives and articulated subtrees (no freejoint on the root) may
182 * use any kind. mj_compile reports the violation if this rule is broken.
183 *
184 * pos:
185 * Offset in the resolved parent frame. For MJCF assets, this is the placement
186 * frame for the asset's first root body.
187 *
188 * fixed:
189 * If true the body is welded to its parent (no freejoint); useful for
190 * static obstacles or fixtures. Ignored when mjcf_path is set.
191 *
192 * Fields without an inline default (size, rgba, mass, friction) must be set
193 * explicitly by the caller. They are arbitrary visual/material/dynamic
194 * choices, not neutral identities, so the API refuses to invent placeholder
195 * values.
196 */
198{
199 std::string name;
200 std::string mjcf_path; // optional MJCF asset; when set, shape/size/mass/friction are ignored
201 AttachTarget attach_to; // placement parent (default: world)
202 Shape shape = Shape::Unspecified; // required for primitives; rejected at build time if not set
203 double size[3]; // half-extents (BOX) / {radius, 0, 0} (SPHERE) / {radius, half-len, 0} (CYL)
204 double pos[3] = { 0.0, 0.0, 0.0 }; // offset in resolved parent frame [m]
205 double quat[4] = { 0.0, 0.0, 0.0, 1.0 }; // orientation offset [x, y, z, w]
206 float rgba[4]; // [r, g, b, a]; required for primitives
207 bool fixed = false;
208 double mass; // [kg]; required for primitives
210 double friction[3]; // [slide, spin, roll]; required for primitives
211};
212
213/**
214 * @ingroup grp_types
215 * A frame to mark on a body of the assembled scene, as a MuJoCo site.
216 *
217 * Sites are what a model's own frames become: the scene states where they sit, and the
218 * runtime can then address and draw them. Added after every robot and object, so `body`
219 * may name anything the assembled scene holds. A site the asset already declares under
220 * this name is left alone -- the asset's own is authoritative.
221 */
223{
224 std::string body; // body to add the site to, by name
225 std::string name; // site name, unique within the scene
226 double pos[3] = { 0.0, 0.0, 0.0 }; // offset in the body frame [m]
227 double quat[4] = { 0.0, 0.0, 0.0, 1.0 }; // orientation in the body frame [x, y, z, w]
228};
229
230/**
231 * @ingroup grp_types
232 * A named fixed camera to add to the world body of the scene.
233 * After build_scene() the camera is accessible by name via get_camera_names()
234 * and can be activated on a Viewer or VideoRecorder with use_camera().
235 *
236 * pos and fovy have no defaults: there is no neutral camera position or
237 * field of view, so the caller must specify both. quat defaults to identity.
238 */
240{
241 std::string name;
242 std::string body; // anchor body; empty = worldbody
243 double pos[3]; // position in the anchor body's frame [m]
244 double quat[4] = { 0.0, 0.0, 0.0, 1.0 }; // orientation [x, y, z, w]
245 double fovy; // vertical field of view [degrees]
246};
247
248/** @ingroup grp_types
249 * Full scene description passed to build_scene().
250 * timestep, add_floor, and add_skybox have no defaults: the caller must
251 * choose a physics step and an explicit yes/no for each decoration so the
252 * resulting scene is never silently misconfigured. gravity_z defaults to
253 * Earth gravity. */
255{
256 std::vector<RobotSpec> robots;
257 double timestep; // required; suggested 0.002 [s]
258 double gravity_z = -9.81; // Earth gravity [m/s^2]
259 bool add_floor; // required; checker groundplane geom
260 double floor_z = 0.0; // floor plane height in the world frame [m]
261 bool add_skybox; // required; gradient sky + directional light
262 std::vector<SceneObject> objects;
263 std::vector<SiteSpec> sites; // frames marked on the assembled scene's bodies
264 std::vector<CameraSpec> cameras; // static world cameras added to worldbody
265};
266
267/**
268 * @ingroup grp_types
269 * Logical force-torque sensor backed by MuJoCo's separate <force> and <torque>
270 * sensors. If force_sensor/torque_sensor are omitted, init_robot_from_mjcf()
271 * resolves "{name}_force" and "{name}_torque".
272 */
274{
275 const char *name = nullptr; // logical wrapper name
276 const char *force_sensor = nullptr; // MuJoCo <force> sensor name
277 const char *torque_sensor = nullptr; // MuJoCo <torque> sensor name
278 const char *frame_site = nullptr; // optional site that defines the sensor frame
279};
280
281/**
282 * @ingroup grp_types
283 * Runtime force-torque sensor state. The wrench is updated by update().
284 */
286{
287 std::string name;
288 std::string force_sensor;
289 std::string torque_sensor;
290 std::string frame_site;
291
292 int force_adr = -1;
293 int torque_adr = -1;
295
296 KDL::Wrench wrench = KDL::Wrench::Zero();
297};
298
299/**
300 * @ingroup grp_types
301 * Optional tool/end-effector description used while building the KDL chain.
302 *
303 * tool_body names the root of the attached tool subtree whose mass/inertia is
304 * lumped into the arm dynamics. tcp_site names an authored MuJoCo site that
305 * becomes the KDL terminal frame for FK/IK (takes priority when set). When
306 * the model has no suitable site, tcp_frame provides an equivalent manual
307 * transform expressed in the tip body's local frame.
308 * For the prefixed Robotiq 2F-85 this is typically {"g_base", "g_pinch"}.
309 */
311{
312 const char *tool_body = nullptr;
313 const char *tcp_site = nullptr; // MuJoCo site name (takes priority)
314 KDL::Frame tcp_frame = KDL::Frame::Identity(); // manual TCP in tip frame (fallback)
315 std::vector<ForceTorqueSensorSpec> ft_sensors;
316};
317
318/**
319 * @ingroup grp_types
320 * Joint-space control mode for update().
321 * POSITION - writes jnt_pos_cmd to actuator ctrl inputs.
322 * TORQUE - writes jnt_trq_cmd to qfrc_applied (generalized forces).
323 */
324enum class CtrlMode { POSITION, TORQUE };
325
326/**
327 * @ingroup grp_types
328 * Runtime handle for one KDL-tracked articulation inside a MuJoCo scene.
329 * model/data are borrowed (never freed by cleanup()); call destroy_scene() separately.
330 *
331 * Workflow:
332 * 1. Call init_robot_from_mjcf() - populates configuration and sizes port vectors to n_joints.
333 * 2. Each control step: read *_msr ports (updated by update()), fill *_cmd ports,
334 * call update() to apply commands to MuJoCo and read back sensor state.
335 */
336struct Robot
337{
338 /* Configuration - set once by init_robot() / init_from_mjcf(). */
339 mjModel *model = nullptr;
340 mjData *data = nullptr;
341 KDL::Chain chain;
342 KDL::Frame tip_T_tcp = KDL::Frame::Identity();
343 bool has_tcp_frame = false;
344 std::string tcp_site;
345 int n_joints = 0;
346 std::vector<std::string> joint_names;
347 std::vector<std::pair<double, double>> joint_limits;
348 std::vector<ForceTorqueSensor> ft_sensors;
349
350 /* Ports - read/written each control cycle. */
352 bool paused = false;
353 std::vector<double> jnt_pos_msr; // [rad] - measured joint positions (written by update())
354 std::vector<double> jnt_vel_msr; // [rad/s] - measured joint velocities (written by update())
355 std::vector<double> jnt_trq_msr; // [Nm] - actuator output torques (written by update())
356 std::vector<double> jnt_pos_cmd; // [rad] - position setpoints (POSITION mode)
357 std::vector<double> jnt_trq_cmd; // [Nm] - torque commands (TORQUE mode)
358
359 /* Internal state - populated by init_robot() / init_from_mjcf(). */
360 std::vector<int> kdl_to_mj_qpos; // KDL index -> MuJoCo qpos address
361 std::vector<int> kdl_to_mj_dof; // KDL index -> MuJoCo dof address
362 std::vector<int> kdl_to_mj_ctrl; // KDL index -> MuJoCo ctrl index (-1 if none)
363};
364
365/**
366 * @ingroup grp_viewer
367 * GLFW window and MuJoCo visualization state for the manual render loop.
368 * Created by init_window(); freed by cleanup(Viewer *).
369 */
370struct Viewer
371{
372 GLFWwindow *window = nullptr;
373 mjvScene scn{};
374 mjvCamera cam{};
375 mjvOption opt{};
376 mjvPerturb pert{};
377 mjrContext con{};
378 /* Real-time factor controlling simulation speed in step()/tick().
379 * 1.0 = real-time (default), 2.0 = 2x faster, 0.5 = half speed.
380 * Keyboard: ',' slows down, '.' speeds up in both viewer modes.
381 * 0.0 means run as fast as possible (no sleep). */
382 double realtime_factor = 1.0;
383 /* internal: real-time pacing state used by tick(). */
384 std::chrono::steady_clock::time_point _tick_t{};
385 /* internal: non-null when init_window_sim() is used; holds SimUiState*. */
386 void *_sim_ui = nullptr;
387};
388
389/**
390 * @ingroup grp_recorder
391 * Standard output resolution presets for init_video_recorder().
392 * Each maps to a 16:9 frame size at the named quality level.
393 */
394enum class VideoResolution {
395 R360p = 360, // 640 x 360
396 R480p = 480, // 854 x 480
397 R720p = 720, // 1280 x 720
398 R1080p = 1080, // 1920 x 1080
399 R2K = 1440, // 2560 x 1440
400 R4K = 2160, // 3840 x 2160
401};
402
403/**
404 * @ingroup grp_recorder
405 * Headless video recorder. Renders frames to an EGL offscreen buffer and
406 * pipes raw RGB data to an ffmpeg process, producing an H.264 MP4 without a
407 * display server or GLFW window.
408 *
409 * Requirements: EGL (libegl-dev) and ffmpeg available in PATH.
410 *
411 * Typical usage:
412 *
413 * VideoRecorder vr;
414 * init_video_recorder(&vr, model, "sim.mp4", VideoResolution::R1080p);
415 * vr.cam.azimuth = 135; vr.cam.elevation = -20; vr.cam.distance = 2.5;
416 *
417 * for (int i = 0; i < steps; ++i) {
418 * mj_step(model, data);
419 * record_frame(&vr, model, data);
420 * }
421 *
422 * cleanup(&vr);
423 */
425{
426 mjvCamera cam{}; // camera configuration; modify freely between frames
427 mjvOption opt{}; // rendering options; modify freely between frames
428 void *_impl = nullptr; // opaque EGL + ffmpeg state
429};
430
431struct Env;
432
433/** @ingroup grp_env
434 * Options controlling an environment reset. */
436{
437 int keyframe = 0; // keyframe index to use when available
438 bool use_keyframe = true; // fall back to mj_resetData when false or invalid
439};
440
441/** @ingroup grp_env
442 * Information returned by reset(). */
444{
445 bool used_keyframe = false;
446 int keyframe = -1;
447};
448
449/** @ingroup grp_env
450 * Runtime context passed to Env::on_reset after MuJoCo data has been reset and
451 * before mj_forward() and robot command-port synchronisation. */
453{
454 Env *env = nullptr;
455 mjModel *model = nullptr;
456 mjData *data = nullptr;
457 const ResetOptions *options = nullptr;
458 ResetInfo *info = nullptr;
459};
460
461using ResetHook = std::function<void(ResetContext *)>;
462
463/** @ingroup grp_env
464 * Runtime environment instance: declarative SceneSpec plus compiled MuJoCo
465 * model/data and Robot handles that should be synchronised after reset.
466 *
467 * Env owns model/data created by init_env(); registered Robot pointers are
468 * borrowed and are never deleted by cleanup(Env *). Robot::model/data remain
469 * borrowed aliases for compatibility with the existing robot-centric API.
470 */
471struct Env
472{
474 mjModel *model = nullptr;
475 mjData *data = nullptr;
476 std::vector<Robot *> robots;
478};
479
480/**
481 * @ingroup grp_scene
482 * Save the compiled model to an MJCF XML file for later reloading via build_scene().
483 * Must be called with the model returned by the most recent build_scene() call -
484 * MuJoCo only retains the last compiled model's XML internally.
485 * Typical use: build a combined scene (dual-arm, arm+gripper, ...) once, save it,
486 * then reload via build_scene() in subsequent runs to skip all build steps.
487 * @param model Model to save; must be the most recently compiled model.
488 * @param path Output path for the MJCF XML file.
489 * @return true on success.
490 */
491bool save_model_xml(const mjModel *model, const char *path);
492
493/**
494 * @ingroup grp_robot
495 * Build KDL chain from a compiled MuJoCo model and optional tool/TCP metadata.
496 *
497 * If tool->tcp_site is set, that authored site becomes the KDL terminal frame
498 * for FK/IK. The joint count and MuJoCo joint/actuator maps still cover only
499 * the controllable joints from base_body to tip_body.
500 * Pass tool = nullptr (the default) for an arm with no attached tool.
501 */
503 Robot *r,
504 mjModel *model,
505 mjData *data,
506 const char *base_body,
507 const char *tip_body,
508 const char *prefix = "",
509 const ToolFrameSpec *tool = nullptr
510);
511
512/**
513 * @ingroup grp_robot
514 * Adopt an externally built KDL chain and wire it to a compiled MuJoCo model.
515 *
516 * Same as init_robot_from_mjcf(), except the chain is supplied rather than derived
517 * from the model: use this when the chain comes from the scene description that also
518 * produced the MJCF, so the solvers compute with the authored dynamics. The chain is
519 * taken as given - no tool inertia is lumped onto it, since such a chain already
520 * carries its tool as explicit segments.
521 *
522 * joint_names lists the MuJoCo joint names in KDL chain order, one per chain joint;
523 * they drive the same qpos/dof/ctrl index maps init_robot_from_mjcf() builds, and
524 * prefix is applied to each as there. tool is used only to resolve FT sensors;
525 * tool->tool_body and tool->tcp_site are ignored.
526 */
528 Robot *r,
529 mjModel *model,
530 mjData *data,
531 const KDL::Chain &chain,
532 const std::vector<std::string> &joint_names,
533 const char *prefix = "",
534 const ToolFrameSpec *tool = nullptr
535);
536
537/** @ingroup grp_robot Find a configured logical force-torque sensor by name. */
538const ForceTorqueSensor *find_ft_sensor(const Robot *r, const char *name);
539
540/**
541 * @ingroup grp_robot
542 * Per-joint torque/force saturation limit in KDL joint order, read from the
543 * MuJoCo actuator forcerange (`mjModel::actuator_forcerange`). For each KDL
544 * joint, the returned bound is symmetric: max(|lo|, |hi|) of the driving
545 * actuator's forcerange. Joints with no driving actuator
546 * (`kdl_to_mj_ctrl[i] == -1`) or an unlimited actuator (`actuator_forcelimited`
547 * false) fall back to `fallback`.
548 *
549 * @param r Initialized robot (init_robot_from_mjcf() already called).
550 * @param fallback Bound used for joints without a force-limited actuator;
551 * default is a large, effectively non-limiting value.
552 * @return One entry per KDL joint, same order as r->joint_names.
553 */
554std::vector<double> joint_force_limits(const Robot *r, double fallback = 1e6);
555
556/**
557 * @ingroup grp_scene
558 * Apply one attachment to an arm spec using the MuJoCo spec API (mjs_attach).
559 * Parses a->mjcf_path, attaches its first root body under a->attach_to with the given
560 * pos/quat offset, prefixes all element names with a->prefix, and registers contact
561 * exclusions via mjs_addExclude. Can be called repeatedly to build a chain: each
562 * subsequent a->attach_to may reference any body added by prior calls.
563 * @param[in,out] robot_spec Accumulated robot spec to attach into.
564 * @param[in] a Attachment; a->mjcf_path must not be null.
565 * @return true on success.
566 */
567bool attach_to_spec(mjSpec *robot_spec, const AttachmentSpec *a);
568
569/**
570 * @ingroup grp_scene
571 * Build a MuJoCo scene from one or more robots using the MuJoCo spec API.
572 * This is the primary scene-building function.
573 *
574 * For each RobotSpec: mj_parseXML loads the root MJCF, then attach_to_spec() applies
575 * each entry in RobotSpec::attachments in order (mount, sensor, gripper, etc.),
576 * and mjs_attach places the complete robot spec at the given position. A single
577 * mj_compile produces the final model -- no intermediate XML files are written.
578 *
579 * @param[out] out_model Newly allocated MuJoCo model; caller frees via destroy_scene().
580 * @param[out] out_data Newly allocated MuJoCo data; caller frees via destroy_scene().
581 * @param[in] spec Scene description: robots (with attachment chains), table,
582 * objects, timestep, gravity, floor, skybox.
583 * @return true on success.
584 */
585bool build_scene(mjModel **out_model, mjData **out_data, const SceneSpec *spec);
586
587/**
588 * @ingroup grp_scene
589 * Free a model/data pair allocated by any scene-building function.
590 * @param[in] model Model to free (may be null).
591 * @param[in] data Data to free (may be null).
592 */
593void destroy_scene(mjModel *model, mjData *data);
594
595/**
596 * @ingroup grp_env
597 * Build a runtime environment from a declarative SceneSpec.
598 * The resulting model/data are owned by env and freed by cleanup(Env *).
599 */
600bool init_env(Env *env, const SceneSpec *spec);
601
602/**
603 * @ingroup grp_env
604 * Register a Robot handle to be synchronised after environment reset.
605 * The robot is borrowed; the Env does not delete or clean it up.
606 */
607void env_add_robot(Env *env, Robot *robot);
608
609/**
610 * @ingroup grp_env
611 * Reset the whole environment to a keyframe/default state, call Env::on_reset
612 * for user-specific robot/object/task restoration, then sync all registered
613 * Robot command ports and clear stale robot forces.
614 */
615ResetInfo reset(Env *env, const ResetOptions *options = nullptr);
616
617
618/**
619 * @ingroup grp_viewer
620 * Open a GLFW window and initialise MuJoCo visualization contexts.
621 * Must be called after init_robot() or init_from_mjcf().
622 * @param[out] v Viewer to initialise; must be zero-initialised before call.
623 * @param[in] r Robot whose model drives the rendering context.
624 * @param[in] title Window title string.
625 * @param[in] width Window width in pixels.
626 * @param[in] height Window height in pixels.
627 * @return true on success, false if GLFW or MuJoCo context creation fails.
628 */
630 Viewer *v,
631 Robot *r,
632 const char *title = "MuJoCo",
633 int width = 1280,
634 int height = 720
635);
636
637/**
638 * @ingroup grp_viewer
639 * Open the full MuJoCo simulate UI (panels, physics controls, joint viewer)
640 * in a background render thread, then return so the caller can drive the
641 * physics loop with tick().
642 *
643 * Use this instead of init_window() when you want the simulate UI panels
644 * alongside a user-owned loop. tick() automatically acquires the render
645 * mutex, steps physics, and handles pause / perturbation / speed controls.
646 *
647 * Note: the render thread owns the GLFW window; on Linux (X11 / Wayland)
648 * this works correctly. Not supported on macOS.
649 *
650 * @param[out] v Viewer to initialise; freed by cleanup(Viewer *).
651 * @param[in] r Robot to simulate. r is registered globally; pass the same
652 * Robot to every subsequent step() call so that keyboard and
653 * mouse perturbation callbacks operate on the correct model.
654 * Only one (Viewer, Robot) pair may be active at a time.
655 * @param[in] title Label shown in the window title bar (default "MuJoCo").
656 * @return true on success.
657 */
658bool init_window_sim(Viewer *v, Robot *r, const char *title = "MuJoCo");
659
660/**
661 * @ingroup grp_viewer
662 * Open the simulate UI for a robot-less model/data pair (e.g. a Scene or Env
663 * with no Robot). Physics, camera and pause are handled by the UI directly.
664 * @return true on success.
665 */
666bool init_window_sim(Viewer *v, mjModel *m, mjData *d, const char *title = "MuJoCo");
667
668/**
669 * @ingroup grp_viewer
670 * Reset the viewer's user-scene geom count to 0.
671 * Call once per frame before appending trace segments with add_trace_segment().
672 * No-op when v is not backed by an init_window_sim() window (e.g. headless).
673 * @param[in,out] v Viewer initialised by init_window_sim().
674 */
676
677/**
678 * @ingroup grp_viewer
679 * Append a single line segment to the viewer's user scene. Thread-safe.
680 * The render thread merges the user scene into each frame automatically.
681 * Silently drops the segment once the user-scene geom buffer is full.
682 * No-op when v is not backed by an init_window_sim() window (e.g. headless).
683 * @param[in,out] v Viewer initialised by init_window_sim().
684 * @param[in] a Segment start point (world frame) [m].
685 * @param[in] b Segment end point (world frame) [m].
686 * @param[in] rgba Optional [r, g, b, a] colour; nullptr -> warm orange.
687 */
689 Viewer *v,
690 const KDL::Vector &a,
691 const KDL::Vector &b,
692 const float rgba[4] = nullptr
693);
694
695/**
696 * @ingroup grp_viewer
697 * Append a world-space arrow to the viewer's user scene. Thread-safe.
698 * Shares the user scene with add_trace_segment(), so clear_trace() clears both
699 * and one call per frame is enough for either.
700 * dir need not be normalised; a zero-length dir draws nothing.
701 * Silently drops the arrow once the user-scene geom buffer is full.
702 * No-op when v is not backed by an init_window_sim() window (e.g. headless).
703 * @param[in,out] v Viewer initialised by init_window_sim().
704 * @param[in] from Arrow tail (world frame) [m].
705 * @param[in] dir Direction the arrow points; normalised internally.
706 * @param[in] length Arrow length [m].
707 * @param[in] rgba Optional [r, g, b, a] colour; nullptr -> warm orange.
708 */
710 Viewer *v,
711 const KDL::Vector &from,
712 const KDL::Vector &dir,
713 double length,
714 const float rgba[4] = nullptr
715);
716
717/**
718 * @ingroup grp_robot
719 * Zero all Robot fields. Does not free model or data; call destroy_scene() for that.
720 * @param[in,out] r Robot to tear down.
721 */
722void cleanup(Robot *r);
723
724/**
725 * @ingroup grp_env
726 * Destroy model/data owned by Env and clear borrowed Robot registrations.
727 * Registered Robot objects are not deleted.
728 */
729void cleanup(Env *env);
730
731/**
732 * @ingroup grp_viewer
733 * Release the GLFW window and MuJoCo visualization contexts owned by v.
734 * @param[in,out] v Viewer to tear down; all pointers set to null afterwards.
735 */
737
738/**
739 * @ingroup grp_recorder
740 * Initialise a headless EGL video recorder.
741 * Creates an EGL context, an offscreen render target, and launches an ffmpeg
742 * process (H.264/MP4) via a pipe. The MuJoCo model is used to size the scene
743 * and initialise the rendering context; it must remain valid until cleanup().
744 *
745 * @param vr VideoRecorder to initialise; freed by cleanup(VideoRecorder*).
746 * @param model MuJoCo model for the rendering context.
747 * @param out_path Output MP4 path (e.g. "sim.mp4").
748 * @param width Frame width in pixels (default 1280).
749 * @param height Frame height in pixels (default 720).
750 * @param fps Playback frame rate (default 60).
751 * @return true on success; false if EGL init or ffmpeg launch fails.
752 */
754 VideoRecorder *vr,
755 mjModel *model,
756 const char *out_path,
757 int width = 1280,
758 int height = 720,
759 int fps = 60
760);
761
762/**
763 * @ingroup grp_recorder
764 * Convenience overload: initialise a VideoRecorder using a named resolution preset.
765 * Frame width is derived from the preset at 16:9 aspect ratio.
766 *
767 * @param vr VideoRecorder to initialise.
768 * @param model MuJoCo model.
769 * @param out_path Output MP4 path.
770 * @param resolution VideoResolution preset (e.g. VideoResolution::R1080p).
771 * @param fps Playback frame rate (default 60).
772 * @return true on success.
773 */
775 VideoRecorder *vr,
776 mjModel *model,
777 const char *out_path,
778 VideoResolution resolution,
779 int fps = 60
780);
781
782/**
783 * @ingroup grp_recorder
784 * Render the current simulation state and write one frame to the video stream.
785 * Call mj_step() (or equivalent) before each record_frame() call.
786 *
787 * @param vr VideoRecorder initialised by init_video_recorder().
788 * @param model MuJoCo model.
789 * @param data MuJoCo data (current state).
790 * @return true on success; false on render or pipe write error.
791 */
792bool record_frame(VideoRecorder *vr, mjModel *model, mjData *data);
793
794/**
795 * @ingroup grp_recorder
796 * Initialise offscreen rendering only: EGL context and MuJoCo render buffers,
797 * no ffmpeg process and no output file. Use with render_rgb() to grab frames;
798 * use_camera(VideoRecorder*, ...) and cleanup(VideoRecorder*) work unchanged.
799 *
800 * @param vr VideoRecorder to initialise; freed by cleanup(VideoRecorder*).
801 * @param model MuJoCo model for the rendering context.
802 * @param width Frame width in pixels.
803 * @param height Frame height in pixels.
804 * @return true on success; false if EGL init fails.
805 */
806bool init_offscreen(VideoRecorder *vr, mjModel *model, int width, int height);
807
808/**
809 * @ingroup grp_recorder
810 * Render the current simulation state into a caller-owned top-down RGB8 buffer
811 * of width*height*3 bytes.
812 *
813 * @param vr VideoRecorder initialised by init_offscreen() or init_video_recorder().
814 * @param model MuJoCo model.
815 * @param data MuJoCo data (current state).
816 * @param out Destination buffer, width*height*3 bytes.
817 * @return true on success.
818 */
819bool render_rgb(VideoRecorder *vr, mjModel *model, mjData *data, std::uint8_t *out);
820
821/**
822 * @ingroup grp_recorder
823 * Flush the ffmpeg pipe, finalise the MP4, and release all EGL resources.
824 * After this call vr->_impl is null and the VideoRecorder may be discarded.
825 *
826 * @param vr VideoRecorder to tear down.
827 */
829
830/**
831 * @ingroup grp_robot
832 * Advance one physics timestep.
833 *
834 * Headless (no viewer active): calls mj_step() and returns true.
835 * GUI (init_window_sim() was called): advances physics, renders, syncs to real
836 * time, and polls GLFW events -- exactly what tick() used to do. Returns false
837 * once the user closes the window.
838 *
839 * This replaces the old headless/GUI split:
840 *
841 * // Before:
842 * if (headless) { mj_kdl::step(&r); }
843 * else if (!mj_kdl::tick(&v, m, d)) break;
844 *
845 * // After:
846 * if (!mj_kdl::step(&r)) break;
847 *
848 * Coupling note: in GUI mode the keyboard and mouse perturbation callbacks
849 * operate on the Robot registered via init_window_sim(). Always pass the
850 * same Robot to both init_window_sim() and step(); passing a different Robot
851 * causes perturbation forces to be applied to the wrong model.
852 *
853 * @param[in,out] s Simulation state; must be the Robot passed to init_window_sim().
854 * @return true while the window is open (or always true in headless mode).
855 */
856bool step(Robot *s);
857
858/**
859 * @ingroup grp_robot
860 * Advance the simulation by n timesteps.
861 * Returns false immediately if the viewer window is closed mid-sequence.
862 * @param[in,out] s Simulation state.
863 * @param[in] n Number of steps.
864 */
865bool step_n(Robot *s, int n);
866
867/**
868 * @ingroup grp_viewer
869 * Model/data overload of step() for multi-robot or no-robot GUI loops.
870 * Equivalent to the former tick(Viewer*, mjModel*, mjData*).
871 * @param[in,out] v Viewer initialised by init_window() or init_window_sim().
872 * @param[in] m Shared MuJoCo model.
873 * @param[in] d Shared MuJoCo data.
874 * @return true while the window is open; false once the user closes it.
875 */
876bool step(Viewer *v, mjModel *m, mjData *d);
877
878/**
879 * @ingroup grp_viewer
880 * Sleeps until this step's share of wall time has elapsed, so a loop with no timing of its own
881 * runs at the viewer's real-time factor.
882 *
883 * step() never sleeps: pacing is the caller's job. A loop that already paces itself must not
884 * call this -- it reads realtime_factor_of() and scales its own period instead, so that only
885 * one component owns the loop's timing.
886 * @param[in,out] v Viewer whose real-time factor and last tick time are used.
887 * @param[in] m Shared MuJoCo model, for its timestep.
888 */
889void pace_realtime(Viewer *v, const mjModel *m);
890
891/**
892 * @ingroup grp_viewer
893 * The viewer's current real-time factor, as the user has set it with the speed keys.
894 * @param[in] v Viewer, or nullptr.
895 * @return the factor; 0.0 means uncapped ("RTF: MAX"), 1.0 if @p v is nullptr.
896 */
897double realtime_factor_of(const Viewer *v);
898
899/**
900 * @ingroup grp_viewer
901 * Paces a loop that drives a Robot, using the viewer the library holds.
902 * Does nothing when the run has no viewer, so a headless path needs no branch.
903 * @param[in,out] r Robot being stepped.
904 */
906
907/**
908 * @ingroup grp_viewer
909 * Returns true if the viewer window is open and not scheduled for closing.
910 * @param[in] v Viewer created by init_window().
911 */
912bool is_running(const Viewer *v);
913
914/**
915 * @ingroup grp_viewer
916 * Whether a key is currently held down in the viewer's window.
917 *
918 * The simulate UI opened by init_window_sim() owns its GLFW window on the
919 * render thread, so a caller driving physics on its own thread must not call
920 * glfwGetKey() itself. This reads the key state that the UI's own key callback
921 * records, which is safe from any thread.
922 *
923 * For a window opened by init_window() this forwards to glfwGetKey() and must
924 * therefore be called from the thread that owns the window, as GLFW requires.
925 *
926 * Keys the UI consumes for itself (',' and '.' for the speed control) are
927 * reported like any other.
928 *
929 * @param[in] v Viewer, or nullptr.
930 * @param[in] glfw_key A GLFW key code, e.g. GLFW_KEY_UP.
931 * @return true while the key is held; false for a nullptr or headless viewer,
932 * or an out-of-range key code.
933 */
934bool key_pressed(const Viewer *v, int glfw_key);
935
936/**
937 * @ingroup grp_viewer
938 * Claim a key for the caller, so the simulate UI never acts on it.
939 *
940 * The UI binds keys of its own: the left and right arrows scrub the history
941 * and single-step, escape restores the free camera, space pauses. A caller
942 * that drives a robot with those keys would otherwise fight the UI for them.
943 * A captured key is still reported by key_pressed(); it is only withheld from
944 * the UI's own handler.
945 *
946 * Has no effect on a window opened by init_window(), which has no UI to
947 * withhold the key from.
948 *
949 * @param[in,out] v Viewer initialised by init_window_sim(), or nullptr.
950 * @param[in] glfw_key A GLFW key code, e.g. GLFW_KEY_LEFT.
951 * @param[in] capture true to claim the key, false to give it back.
952 */
953void capture_key(Viewer *v, int glfw_key, bool capture = true);
954
955/**
956 * @ingroup grp_viewer
957 * Render the current simulation frame to the viewer window.
958 * @param[in,out] v Viewer created by init_window().
959 * @param[in] r Robot whose model and data are rendered.
960 * @return true if the window is still open after rendering.
961 */
962bool render(Viewer *v, const Robot *r);
963
964/**
965 * @ingroup grp_viewer
966 * Render the current simulation frame to the viewer window.
967 * Model/data overload -- use when no single Robot owns the scene (e.g. multi-robot).
968 * @param[in,out] v Viewer created by init_window().
969 * @param[in] m MuJoCo model.
970 * @param[in] d MuJoCo data.
971 * @return true if the window is still open after rendering.
972 */
973bool render(Viewer *v, mjModel *m, mjData *d);
974
975/**
976 * @ingroup grp_robot
977 * One control cycle: read MuJoCo into *_msr, then apply *_cmd to MuJoCo.
978 * Read step: qpos -> jnt_pos_msr, qvel -> jnt_vel_msr, qfrc_actuator -> jnt_trq_msr.
979 * Apply step: POSITION -> data->ctrl,
980 * TORQUE -> qfrc_applied; also sets ctrl = qpos to neutralize
981 * position actuators (zeroes kp*(ctrl-qpos) restoring force).
982 * Joints with kdl_to_mj_ctrl[i] == -1 are skipped for ctrl writes.
983 */
984void update(Robot *r);
985
986/**
987 * @ingroup grp_robot
988 * Write KDL joint positions into MuJoCo qpos (KDL chain order -> MuJoCo addresses).
989 * @param[in,out] r Robot with a valid data pointer.
990 * @param[in] q Joint positions in KDL chain order; size must equal r->n_joints.
991 * @param[in] call_forward If true (default), calls mj_forward() after writing qpos
992 * so that body poses and sensor data are updated immediately.
993 */
994void set_joint_pos(Robot *r, const KDL::JntArray &q, bool call_forward = true);
995
996/**
997 * @ingroup grp_robot
998 * Teleport a free-floating body to a world-frame position and optionally a
999 * world-frame orientation, then zero its velocity.
1000 * body_name must identify a body that owns a mjJNT_FREE joint.
1001 * quat is MuJoCo convention [w, x, y, z]; pass nullptr to keep identity orientation.
1002 * @param[in,out] model MuJoCo model.
1003 * @param[in,out] data MuJoCo data.
1004 * @param[in] body_name Name of the free-floating body to teleport.
1005 * @param[in] pos World-frame position [x, y, z].
1006 * @param[in] quat World-frame orientation [w, x, y, z], or nullptr for identity.
1007 */
1009 mjModel *model,
1010 mjData *data,
1011 const char *body_name,
1012 const double pos[3],
1013 const double *quat = nullptr
1014);
1015
1016/**
1017 * @ingroup grp_scene
1018 * Add an object to the scene by appending it to spec->objects and rebuilding
1019 * the model. The old model/data are freed; new ones replace them.
1020 * Any Robot handles sharing the old model/data become stale - call init_robot()
1021 * again on the new model/data after this call.
1022 * @param[in,out] model Current model pointer; updated to new model on success.
1023 * @param[in,out] data Current data pointer; updated to new data on success.
1024 * @param[in,out] spec Scene spec; obj is appended to spec->objects.
1025 * @param[in] obj Object to add.
1026 * @return true on success; model/data and spec->objects unchanged on failure.
1027 */
1028bool scene_add_object(mjModel **model, mjData **data, SceneSpec *spec, const SceneObject &obj);
1029
1030/**
1031 * @ingroup grp_scene
1032 * Env overload: adds obj, rebuilds, and re-initialises all robots registered in env.
1033 * env->model, env->data, and each Robot's model/data pointers are updated automatically.
1034 * @return true on success; env unchanged on failure.
1035 */
1036bool scene_add_object(Env *env, const SceneObject &obj);
1037
1038/**
1039 * @ingroup grp_scene
1040 * Remove a named object from the scene by erasing it from spec->objects and
1041 * rebuilding the model. The old model/data are freed; new ones replace them.
1042 * Any Robot handles sharing the old model/data become stale - call init_robot()
1043 * again on the new model/data after this call.
1044 * @param[in,out] model Current model pointer; updated to new model on success.
1045 * @param[in,out] data Current data pointer; updated to new data on success.
1046 * @param[in,out] spec Scene spec; named object removed from spec->objects.
1047 * @param[in] name Name of the object to remove.
1048 * @return true on success; false if name not found or rebuild fails.
1049 */
1050bool scene_remove_object(mjModel **model, mjData **data, SceneSpec *spec, const std::string &name);
1051
1052/**
1053 * @ingroup grp_scene
1054 * Env overload: removes the named object, rebuilds, and re-initialises all robots registered
1055 * in env. env->model, env->data, and each Robot's model/data pointers are updated automatically.
1056 * @return true on success; false if name not found or rebuild fails.
1057 */
1058bool scene_remove_object(Env *env, const std::string &name);
1059
1060/**
1061 * @ingroup grp_scene
1062 * Return the compiled MuJoCo name for a site inside an MJCF-backed SceneObject.
1063 * build_scene() prefixes all MJCF asset element names with obj.name + "_".
1064 */
1065std::string scene_object_site_name(const SceneObject &obj, const char *site_name);
1066
1067/**
1068 * @ingroup grp_scene
1069 * Say that xpos/xmat are current, or that they are not. The frame getters below forward only
1070 * when they are not, so a caller reading many frames per step pays one solve rather than one
1071 * each. step(), reset() and a forwarding set_joint_pos() already report themselves; a caller
1072 * that writes qpos or a body pose behind the wrapper's back must call the stale form.
1073 */
1074void mark_kinematics_fresh(const mjData *data);
1076
1077/**
1078 * @ingroup grp_scene
1079 * Forget any currency recorded against this mjData, because it is about to be freed and the
1080 * allocator may hand its address to the next one. destroy_scene() already does this.
1081 */
1082void mark_kinematics_forgotten(const mjData *data);
1083
1084/**
1085 * @ingroup grp_scene
1086 * Read a named MuJoCo site as a world-frame KDL frame.
1087 * Forwards first only when the kinematics are not already current.
1088 */
1089bool get_site_frame(const mjModel *model, mjData *data, const char *site_name, KDL::Frame *out);
1090
1091/**
1092 * @ingroup grp_scene
1093 * Read a named MuJoCo body as a world-frame KDL frame.
1094 * Forwards first only when the kinematics are not already current.
1095 */
1096bool get_body_frame(const mjModel *model, mjData *data, const char *body_name, KDL::Frame *out);
1097
1098/**
1099 * @ingroup grp_scene
1100 * Read a joint's position (qpos) in physical units (rad or m), by joint name.
1101 * If the name is not a joint, it is treated as an actuator name and resolved to
1102 * its transmission joint (direct joint, or the first tendon-wrapped joint).
1103 */
1104bool get_joint_position(const mjModel *model, mjData *data, const char *name, double *out);
1105
1106/**
1107 * @ingroup grp_scene
1108 * Read a joint's velocity (qvel) in physical units (rad/s or m/s), by joint name.
1109 * Resolves the name exactly as get_joint_position() does.
1110 */
1111bool get_joint_velocity(const mjModel *model, mjData *data, const char *name, double *out);
1112
1113/**
1114 * @ingroup grp_scene
1115 * Return the names of all cameras in a compiled model.
1116 * Includes cameras from robot MJCFs (e.g. the Kinova wrist camera) and any
1117 * cameras added via SceneSpec::cameras.
1118 */
1119std::vector<std::string> get_camera_names(const mjModel *model);
1120
1121/**
1122 * @ingroup grp_viewer
1123 * Switch the viewer to a named fixed camera defined in the model.
1124 * Works for both init_window() and init_window_sim() paths.
1125 * Pass nullptr or an empty string to return to the free camera.
1126 * @return true if the camera name was found; false if not found (viewer unchanged).
1127 */
1128bool use_camera(Viewer *v, const mjModel *model, const char *name);
1129
1130/**
1131 * Configure the viewer's free orbit camera.
1132 */
1134 Viewer *v,
1135 double distance,
1136 double azimuth,
1137 double elevation,
1138 const std::array<double, 3> &lookat
1139);
1140
1141/**
1142 * @ingroup grp_recorder
1143 * Switch the video recorder to a named fixed camera defined in the model.
1144 * @return true if the camera name was found; false if not found (recorder unchanged).
1145 */
1146bool use_camera(VideoRecorder *vr, const mjModel *model, const char *name);
1147
1148/**
1149 * Internal spec-building helpers.
1150 *
1151 * These are used internally by build_scene() but are exposed here for advanced
1152 * callers that construct mjSpec objects directly. They are not part of the
1153 * stable public API and may change between releases.
1154 */
1155
1156/**
1157 * @ingroup grp_advanced
1158 * Add a sky gradient texture and overhead directional light to spec.
1159 * Corresponds to SceneSpec::add_skybox.
1160 */
1161void add_skybox_to_spec(mjSpec *spec);
1162
1163/**
1164 * @ingroup grp_advanced
1165 * Add a checker groundplane texture, material, and floor plane geom to spec.
1166 * Corresponds to SceneSpec::add_floor, placed at floor_z along the world z axis
1167 * so a scene whose world frame is not at ground level still gets a ground.
1168 */
1169void add_floor_to_spec(mjSpec *spec, double floor_z = 0.0);
1170
1171/**
1172 * @ingroup grp_advanced
1173 * Add free-floating or fixed rigid bodies to the world body of spec.
1174 * @param spec MuJoCo spec to modify.
1175 * @param objects List of objects to add.
1176 */
1177void add_objects_to_spec(mjSpec *spec, const std::vector<SceneObject> &objects);
1178
1179/**
1180 * @ingroup grp_advanced
1181 * Compile spec into a model and create its data buffer.
1182 * spec is always deleted (on success and failure).
1183 * @param[in] spec MuJoCo spec to compile; always freed by this call.
1184 * @param[out] out_model Newly allocated model on success; null on failure.
1185 * @param[out] out_data Newly allocated data on success; null on failure.
1186 * @return true on success.
1187 */
1188bool compile_and_make_data(mjSpec *spec, mjModel **out_model, mjData **out_data);
1189
1190/**
1191 * @ingroup grp_advanced
1192 * Load MuJoCo decoder plugins (STL, OBJ, ...) once at first use.
1193 * Required for external mesh decoder plugin libraries.
1194 * Called automatically by all scene-building functions; call explicitly only
1195 * when building a scene via raw mjSpec APIs without going through the library.
1196 */
1198
1199} // namespace mj_kdl
void add_skybox_to_spec(mjSpec *spec)
bool compile_and_make_data(mjSpec *spec, mjModel **out_model, mjData **out_data)
void add_objects_to_spec(mjSpec *spec, const std::vector< SceneObject > &objects)
void ensure_plugins_loaded()
void add_floor_to_spec(mjSpec *spec, double floor_z=0.0)
void env_add_robot(Env *env, Robot *robot)
ResetInfo reset(Env *env, const ResetOptions *options=nullptr)
bool init_env(Env *env, const SceneSpec *spec)
void set_log_level(LogLevel level)
LogLevel get_log_level()
LogLevel g_log_level
bool render_rgb(VideoRecorder *vr, mjModel *model, mjData *data, std::uint8_t *out)
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 init_offscreen(VideoRecorder *vr, mjModel *model, int width, int height)
void set_body_pose(mjModel *model, mjData *data, const char *body_name, const double pos[3], const double *quat=nullptr)
bool step_n(Robot *s, int n)
std::vector< double > joint_force_limits(const Robot *r, double fallback=1e6)
bool init_robot_from_chain(Robot *r, mjModel *model, mjData *data, const KDL::Chain &chain, const std::vector< std::string > &joint_names, const char *prefix="", const ToolFrameSpec *tool=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)
const ForceTorqueSensor * find_ft_sensor(const Robot *r, const char *name)
bool get_site_frame(const mjModel *model, mjData *data, const char *site_name, KDL::Frame *out)
bool get_body_frame(const mjModel *model, mjData *data, const char *body_name, KDL::Frame *out)
bool build_scene(mjModel **out_model, mjData **out_data, const SceneSpec *spec)
bool attach_to_spec(mjSpec *robot_spec, const AttachmentSpec *a)
bool scene_add_object(mjModel **model, mjData **data, SceneSpec *spec, const SceneObject &obj)
bool get_joint_position(const mjModel *model, mjData *data, const char *name, double *out)
bool scene_remove_object(mjModel **model, mjData **data, SceneSpec *spec, const std::string &name)
void destroy_scene(mjModel *model, mjData *data)
std::vector< std::string > get_camera_names(const mjModel *model)
bool save_model_xml(const mjModel *model, const char *path)
bool get_joint_velocity(const mjModel *model, mjData *data, const char *name, double *out)
void mark_kinematics_forgotten(const mjData *data)
std::string scene_object_site_name(const SceneObject &obj, const char *site_name)
void mark_kinematics_fresh(const mjData *data)
bool init_window_sim(Viewer *v, Robot *r, const char *title="MuJoCo")
void pace_realtime(Viewer *v, const mjModel *m)
void capture_key(Viewer *v, int glfw_key, bool capture=true)
bool use_camera(Viewer *v, const mjModel *model, const char *name)
double realtime_factor_of(const Viewer *v)
bool init_window(Viewer *v, Robot *r, const char *title="MuJoCo", int width=1280, int height=720)
void add_overlay_arrow(Viewer *v, const KDL::Vector &from, const KDL::Vector &dir, double length, const float rgba[4]=nullptr)
void clear_trace(Viewer *v)
bool render(Viewer *v, const Robot *r)
bool key_pressed(const Viewer *v, int glfw_key)
void add_trace_segment(Viewer *v, const KDL::Vector &a, const KDL::Vector &b, const float rgba[4]=nullptr)
bool is_running(const Viewer *v)
void mark_kinematics_stale()
std::function< void(ResetContext *)> ResetHook
void set_free_camera(Viewer *v, double distance, double azimuth, double elevation, const std::array< double, 3 > &lookat)
std::vector< std::pair< std::string, std::string > > contact_exclusions
ResetHook on_reset
std::vector< Robot * > robots
const ResetOptions * options
std::vector< AttachmentSpec > attachments
std::vector< ForceTorqueSensor > ft_sensors
std::vector< int > kdl_to_mj_ctrl
std::vector< int > kdl_to_mj_qpos
std::vector< int > kdl_to_mj_dof
std::vector< double > jnt_pos_msr
std::string tcp_site
std::vector< double > jnt_pos_cmd
std::vector< std::pair< double, double > > joint_limits
std::vector< double > jnt_vel_msr
std::vector< double > jnt_trq_cmd
std::vector< double > jnt_trq_msr
std::vector< std::string > joint_names
KDL::Frame tip_T_tcp
std::vector< RobotSpec > robots
std::vector< SceneObject > objects
std::vector< SiteSpec > sites
std::vector< CameraSpec > cameras
std::vector< ForceTorqueSensorSpec > ft_sensors
GLFWwindow * window
std::chrono::steady_clock::time_point _tick_t