|
Mujoco KDL Wrapper
0.5.1
MuJoCo + KDL bridge for robot kinematics and dynamics
|
Full reference for building mj_kdl_wrapper in a ROS 2 (Jazzy) colcon workspace, for both C++ and Python. For non-ROS builds, see the Standalone Installation Guide.
This is a plain CMake project, not a ROS package: it has no package.xml. colcon identifies it from CMakeLists.txt and reads its dependencies from the find_package() calls, so it orders the wrapper after the orocos_kdl package. rosdep has nothing to resolve; install the system packages with apt (see System packages).
It is not registered in the ament index, so ros2 pkg list / ros2 pkg prefix will not show it - expected, and it does not affect linking: dependent packages consume it with find_package(mj_kdl_wrapper), and a downstream <depend>mj_kdl_wrapper</depend> orders the build correctly.
This project requires the secorolab Orocos KDL fork (not the system KDL). In a standalone build the wrapper builds and bundles its own private copy. In ROS 2 that is unsafe: ROS ships the distro Orocos KDL and python3-pykdl with the same SONAME (liborocos-kdl.so.1.5) and PyKDL module name, so a single process can hold only one copy - the loader keeps the first and silently drops the other's symbols.
The fix is to build the secorolab fork as its own workspace package and have every package (including mj_kdl_wrapper) consume that one shared liborocos-kdl. The fork ships package.xml for orocos_kdl (C++, build type cmake) and python_orocos_kdl, so colcon can build it directly.
ROS 2 (Jazzy or Lyrical) and colcon (via python3-colcon-common-extensions) are assumed present and sourced. CI builds both distros and runs the camera_ros test there; the rest of the test suite runs in the non-ROS CI.
Create the workspace, clone the wrapper, import the KDL fork from its mj_kdl_wrapper.repos, then build KDL first and the wrapper against it:
Result: mj_kdl_wrapper links install/orocos_kdl/lib/liborocos-kdl.so and ships no KDL of its own; there is exactly one KDL in the overlay.
The wrapper checkout carries a third_party/COLCON_IGNORE, so a fork imported into its own third_party/ for standalone builds is never picked up as a duplicate workspace package.
With rclcpp and sensor_msgs sourced at configure time, the wrapper also builds mj_kdl_wrapper::camera_ros (MJ_KDL_WITH_ROS=AUTO; ON requires ROS, OFF skips it). It publishes a frame rendered with render_rgb() as sensor_msgs/Image plus CameraInfo (include/mj_kdl_wrapper/camera_ros.hpp); it creates no node or thread of its own.
Step 1 must precede step 2 so the orocos_kdl package is on CMAKE_PREFIX_PATH when the wrapper configures. colcon finds orocos_kdl among the wrapper's find_package() calls, so a single colcon build also orders them; the flag MJ_KDL_OROCOS_KDL_FROM_PACKAGE=ON is still needed. A consuming ROS package declares the dependencies in its own package.xml:
rclpy is only importable from the interpreter ROS 2 was built against (the system Python). Create the venv with --system-site-packages so it can import the system rclpy while keeping the wheel and its pinned mujoco==3.14.0 inside the venv:
The wheel bundles its own PyKDL, which takes precedence over the system python3-pykdl on the venv sys.path. To confirm which is loaded:
The fork's python_orocos_kdl is a catkin package and is not part of the C++ overlay above; in ROS 2 the Python side uses the bundled wheel PyKDL (or the system python3-pykdl).
Source ROS 2 and the workspace first, then activate the venv so it inherits the ROS 2 environment:
| Approach | How | When |
|---|---|---|
| find_package (recommended) | -DMJ_KDL_OROCOS_KDL_FROM_PACKAGE=ON | KDL is a colcon package on CMAKE_PREFIX_PATH (the overlay) |
| By prefix | -DMJ_KDL_OROCOS_KDL_INSTALL_DIR=<prefix> | KDL is installed at a known prefix |
| Process isolation | (no build change) | Keep mj_kdl_wrapper and system-KDL packages (kdl_parser, tf2_kdl, robot_state_publisher) in separate nodes; ROS 2 is multi-process, so each loads one KDL |
| Bundled (default) | (no build change) | Single-package use; not safe to mix with system-KDL packages in one process |
For the Python wheel, pass the shared-KDL define through scikit-build-core (--config-settings=cmake.define.MJ_KDL_OROCOS_KDL_INSTALL_DIR=<prefix>); note the prefix must also ship PyKDL.