|
Mujoco KDL Wrapper
0.2.2
MuJoCo + KDL bridge for robot kinematics and dynamics
|
This page collects the C++ wrapper usage notes that are too detailed for the README. For complete function signatures, see the generated Doxygen API pages for include/mj_kdl_wrapper/mj_kdl_wrapper.hpp.
The examples and tests resolve paths through example_paths.hpp (a header-only helper under src/examples/) so no checkout location is hard-coded. It mirrors the Python menagerie resolver:
Populate the cache with cmake -DMJ_KDL_FETCH_MENAGERIE=ON (it clones Menagerie and copies the bundled assets into the cache) or the mj-kdl-fetch-menagerie console script. The same cache backs both the C++ and Python examples.
Overrides: export MJ_KDL_MENAGERIE=/path/to/menagerie to resolve models from a checkout outside the cache. The C++ helper has no per-asset override – assets resolve from the cache only. (The Python examples additionally honor per-file overrides such as MJ_KDL_MODEL and MJ_KDL_GRIPPER; see the Python guide.)
SceneSpec has no defaults for timestep, add_floor, or add_skybox. Those are choices, not values the library can guess. build_scene() rejects timestep <= 0 at runtime. SceneSpec::robots may be empty; object-only scenes are valid.
For an object-only scene, add MJCF or primitive SceneObject entries and leave sc.robots empty:
save_model_xml(model, path) writes the most recently compiled model back to MJCF. Use it when you want to build a combined scene once and reload the merged model later through MuJoCo. destroy_scene(model, data) frees the pair returned by build_scene().
Set wrapper log verbosity globally when debugging scene construction:
The raw mjSpec helpers (add_skybox_to_spec(), add_floor_to_spec(), add_objects_to_spec(), compile_and_make_data(), and ensure_plugins_loaded()) exist for advanced callers that build MuJoCo specs directly. Most users should go through SceneSpec and build_scene() so plugins, decorations, objects, robots, cameras, compilation, and ownership all follow the same path.
Robot borrows model and data; it never frees them. After init, the port vectors are sized to n_joints and ordered like the KDL chain. Use joint_names and joint_limits to inspect that mapping before writing controllers.
When a tool or gripper is attached, pass a ToolFrameSpec so KDL dynamics include the full tool inertia and FK uses the TCP site:
For a wrist force-torque sensor, attach the sensor MJCF first, then attach the gripper to a site exported by that sensor asset:
Then register the logical force-torque sensor through ToolFrameSpec. MuJoCo stores it as separate <force> and <torque> sensors; the wrapper combines one pair into a KDL::Wrench.
When force_sensor and torque_sensor are omitted, the wrapper resolves {name}_force and {name}_torque.
AttachTarget is a tagged pair of AttachKind { World, Body, Site, Frame } and an element name. The Kinova GEN3 MJCF exports pinch_site on the bracelet, which already encodes the tool offset and flip, so a gripper attaches with no manual pos or euler:
Optional pos and euler on the attachment spec are composed with the parent site pose, so you can still add small offsets:
If a model has no suitable site, attach by body name instead:
Chains are supported: push multiple AttachmentSpec entries in order, such as mount, force-torque sensor, then gripper. Each entry's attach_to may reference any body, site, or frame added by prior entries.
SceneObject and RobotSpec share the same attach_to field, so a robot can follow a tabletop site without hand-threading world-frame heights. Build order in build_scene() is decorations, objects in declaration order, robots, then cameras. A robot's attach_to can reference any prior object, and a child object's attach_to can reference any earlier object in SceneSpec::objects.
SceneObject has no defaults for shape, size, rgba, mass, or friction. For MJCF-backed objects, when mjcf_path is set, those fields are ignored at runtime. For primitives, build_scene() checks:
MuJoCo restricts free joints to top-level bodies, so a non-fixed primitive with a free joint must stay world-anchored.
Add fixed world cameras through SceneSpec::cameras. pos and fovy are required; euler defaults to identity.
After building, get_camera_names() returns robot MJCF cameras and cameras added through the scene spec. use_camera() switches a viewer or recorder to a fixed camera; pass nullptr or "" to return to the free camera.
Use get_body_frame() and get_site_frame() to read world poses as KDL::Frame; both call mj_forward() before reading MuJoCo pose arrays. set_body_pose() teleports a free body and zeroes its velocity. The quaternion, when supplied in C++, uses MuJoCo order [w, x, y, z].
update(&robot) does both halves of the port synchronization: it reads MuJoCo joint state into jnt_pos_msr, jnt_vel_msr, and jnt_trq_msr, then applies the command ports. In POSITION mode it writes jnt_pos_cmd to actuator controls. In TORQUE mode it writes jnt_trq_cmd to qfrc_applied and neutralizes position actuators at the current joint positions.
Use set_joint_pos(&robot, q, call_forward) to seed joint state directly in KDL order. With call_forward=true, body/site poses are updated immediately.
reset(Env*) resets the environment runtime to its initial keyframe, calls an optional environment reset hook, re-seeds all registered robots' command ports to the current measured state, and clears stale robot forces. Use the hook to put objects, controllers, and task state back at their episode start values:
Use Env when runtime rebuilds or episode resets should keep robot handles synchronized. Register every borrowed robot with env_add_robot(). Cleanup destroys only the environment-owned model/data and clears registrations:
Interactive recording is available from the Simulate UI:
The recorder camera list includes Current, Free, Tracking, robot MJCF cameras, and cameras added through SceneSpec::cameras.
Prefer the Env overloads when registered robot handles should be re-initialized automatically:
With raw mjModel** / mjData** overloads, any Robot that borrowed the old pointers is stale until you call init_robot_from_mjcf() again. In Python, Scene.add_object(), Scene.remove_object(), Env.add_object(), and Env.remove_object() perform that rebind step for existing Python Robot handles. See Python API guide for Python ownership rules.
init_window_sim() opens the full Simulate UI in a background render thread and lets step(&robot) handle physics, rendering, pause, perturbation, and pacing. For a lower-level GLFW window, use init_window(), drive MuJoCo yourself, and call render():
Use init_window_sim(&viewer, model, data, "object scene") for object-only scenes that still need the full Simulate UI. Use step(&viewer, model, data) for multi-robot or no-robot loops where the viewer should own the same pacing behavior as the Simulate UI path.
| Input | Action |
|---|---|
| Left drag | Orbit camera |
| Right drag | Pan camera |
| Scroll | Zoom |
| Double-click body | Select body, name shown in the Perturb panel |
| D | Deselect body |
| Space | Pause or resume |
| , | Decrease wrapper real-time factor |
| . | Increase wrapper real-time factor |
Applying a perturbation force or torque to the selected body can be done in two ways:
All other controls, including reset, quit, rendering flags, live camera selection, and recording, are in the MuJoCo panels.