MetaSkeleton and ReferentialSkeleton in DART: A Practical Guide for Robotics Developers

MetaSkeleton is a pure abstract interface that defines the complete kinematics and dynamics API for any collection of bodies, while ReferentialSkeleton is a concrete implementation that creates non-owning views onto subsets of one or more Skeletons, enabling generic algorithms to work on partial models or multi-robot assemblies without modification.

The MetaSkeleton and ReferentialSkeleton classes in the dartsim/dart physics engine provide powerful abstractions for writing generic robotics algorithms. These interfaces allow you to write controllers, planners, and simulators that operate on any collection of body nodes and joints—whether that is a full robot model or just a view onto a specific subset—without requiring code changes between different use cases.

What is MetaSkeleton in DART?

MetaSkeleton is a pure abstract base class defined in [dart/dynamics/meta_skeleton.hpp](https://github.com/dartsim/dart/blob/main/dart/dynamics/meta_skeleton.hpp) that specifies the complete public API for querying the structural hierarchy, kinematic state, and dynamic properties of a set of bodies. Because it owns no data itself, it serves as a universal interface that concrete skeleton implementations must satisfy.

Core Capabilities of MetaSkeleton

The interface provides several categories of functionality essential for robotics algorithms:

  • Structural queries – Methods like getNumBodyNodes(), getBodyNode(i), and hasJoint(j) provide read-only or mutable access to the underlying graph of BodyNode, Joint, and DegreeOfFreedom objects. This enables generic iteration over robot components regardless of the concrete container type.

  • State access – Uniform getters and setters such as getPositions() and setCommands() allow algorithms to read and write generalized coordinates, velocities, and forces through a consistent interface.

  • Jacobians and dynamics – The class declares methods including getJacobian(), getWorldJacobian(), getMassMatrix(), and getCOM(), ensuring that the same mathematical operations work identically whether operating on a full robot or a sub-model.

  • Signals and cloningMetaSkeleton provides NameChangedSignal for event notification and cloneMetaSkeleton() for creating independent copies, which is useful for thread-safe simulations or temporary "what-if" modeling.

  • Threading support – The getLockableReference() method returns a LockableReference that protects concurrent access to underlying data structures, making the interface safe for multithreaded simulation environments.

Why Use the Abstract Interface

Algorithms written against MetaSkeletonPtr or MetaSkeleton& are implementation-agnostic. A controller that accepts a MetaSkeleton works unchanged whether it receives a full Skeleton owning all bodies, a Group representing just a robot arm, or a custom view spanning multiple robots. This decouples your algorithmic code from the specific model structure.

What is ReferentialSkeleton in DART?

ReferentialSkeleton is an abstract concrete subclass of MetaSkeleton defined in [dart/dynamics/referential_skeleton.hpp](https://github.com/dartsim/dart/blob/main/dart/dynamics/referential_skeleton.hpp). Unlike Skeleton, which owns its components, ReferentialSkeleton implements the MetaSkeleton API by holding references—via shared_ptr or weak_ptr—to body nodes, joints, and DoFs from one or more existing Skeleton objects.

Key Characteristics of ReferentialSkeleton

  • Non-owning views – The class does not take ownership of the underlying bodies; it merely references them. The original Skeleton objects remain responsible for memory management and lifecycle.

  • Multi-skeleton aggregation – A single ReferentialSkeleton can combine components from several different Skeleton instances, enabling coordinated control of multi-robot systems or bimanual manipulation scenarios.

  • Cache-aware implementation – The class internally caches mass-matrix-related data and provides updateCaches() to refresh these structures when the underlying skeleton changes. Note that getMass() computes total mass in linear time (O(N)) because the view may span multiple skeletons with disjoint structures.

  • Concrete derived classes – Higher-level utilities such as Group and Linkage inherit from ReferentialSkeleton. These classes provide user-friendly constructors while leveraging the view-based architecture.

When to Choose ReferentialSkeleton

Use ReferentialSkeleton when you need to operate on partial models or cross-robot coordination:

  • Sub-model operations – Control or analyze only a subset of a robot (e.g., an arm, hand, or kinematic chain) without copying the entire model or modifying the original Skeleton.

  • Multi-robot views – Create a single interface that combines bodies from different robots, such as treating two independent manipulators as a single dual-arm system for coordinated planning.

  • High-level API building – Build custom "virtual robots" using Group (user-defined collections) or Linkage (fixed-topology subsets) to expose simplified interfaces to motion planners or trajectory optimizers.

Comparing Skeleton Types in DART

Understanding the relationship between these classes helps you select the appropriate abstraction for your use case:

  • SkeletonOwns its BodyNodes, Joints, and DoFs. Use this for full robot models, simulation worlds, and loading from URDF/SDF files.

  • MetaSkeletonOwns nothing (pure interface). Use this as the parameter type for generic algorithms that need to query or control any collection of bodies.

  • ReferentialSkeletonReferences components from one or more Skeletons. Use this for lightweight views, sub-models, or combining multiple robots into a single control interface.

Practical Code Examples

The following examples demonstrate typical workflows using these abstractions.

Example 1: Generic Algorithm Using MetaSkeleton

Write a controller that works on any skeleton implementation:

#include <dart/dynamics/MetaSkeleton.hpp>

// A simple controller that drives all joints to a target position
void setTarget(dart::dynamics::MetaSkeletonPtr ms,
               const Eigen::VectorXd& target)
{
    assert(target.size() == ms->getNumDofs());
    ms->setPositions(target);  // Works for Skeleton or ReferentialSkeleton
}

This function accepts either a full Skeleton or a ReferentialSkeleton view without requiring template parameters or overloaded variants.

Example 2: Building a Group (Concrete ReferentialSkeleton)

Create a view representing just the left arm of a humanoid robot:

#include <dart/dynamics/Group.hpp>
#include <dart/dynamics/Skeleton.hpp>

auto robot = dart::dynamics::Skeleton::create("humanoid");
// ... load URDF/SDF ...

// Create a Group that only contains the right leg
auto rightLeg = std::make_shared<dart::dynamics::Group>("right_leg");

// Register body nodes; joints and DoFs are registered automatically
rightLeg->registerComponent(robot->getBodyNode("r_hip"));
rightLeg->registerComponent(robot->getBodyNode("r_knee"));
rightLeg->registerComponent(robot->getBodyNode("r_ankle"));

// Use the group like a normal skeleton
Eigen::VectorXd q = rightLeg->getPositions();
rightLeg->setCommands(Eigen::VectorXd::Zero(rightLeg->getNumDofs()));

Because Group inherits from ReferentialSkeleton, it provides all Jacobian, dynamics, and state-access methods while operating only on the selected subset.

Example 3: Combining Multiple Robots via Custom ReferentialSkeleton

Implement a custom view that aggregates two independent robots:

#include <dart/dynamics/ReferentialSkeleton.hpp>
#include <dart/dynamics/Skeleton.hpp>

class DualRobotView : public dart::dynamics::ReferentialSkeleton
{
public:
    DualRobotView(dart::dynamics::SkeletonPtr a,
                  dart::dynamics::SkeletonPtr b)
    {
        registerSkeleton(a.get());
        registerSkeleton(b.get());

        // Aggregate all body nodes from both robots
        for (std::size_t i = 0; i < a->getNumBodyNodes(); ++i)
            registerComponent(a->getBodyNode(i));
        for (std::size_t i = 0; i < b->getNumBodyNodes(); ++i)
            registerComponent(b->getBodyNode(i));

        // Refresh internal caches so Jacobians compute correctly
        updateCaches();
    }
};

// Usage
auto robotA = dart::dynamics::Skeleton::create("A");
auto robotB = dart::dynamics::Skeleton::create("B");
auto view = std::make_shared<DualRobotView>(robotA, robotB);
std::cout << "Total DoFs in view: " << view->getNumDofs() << std::endl;

This approach creates a unified MetaSkeleton interface spanning multiple independent models, useful for decentralized control algorithms that treat separate robots as a single kinematic chain.

Summary

  • MetaSkeleton provides a pure abstract interface in meta_skeleton.hpp that decouples algorithms from concrete skeleton implementations, supporting generic controllers and planners.

  • ReferentialSkeleton in referential_skeleton.hpp implements this interface using non-owning references, enabling lightweight views onto partial models or multi-robot assemblies.

  • Use Skeleton for complete robot models that own their data; use MetaSkeleton as the parameter type for generic code; use ReferentialSkeleton (via Group, Linkage, or custom subclasses) when you need views, subsets, or combined robot models.

  • Key methods for views include registerComponent(), registerSkeleton(), and updateCaches() to maintain correct dynamic computations across referenced bodies.

Frequently Asked Questions

What is the difference between Skeleton and MetaSkeleton in DART?

Skeleton is a concrete class that owns its BodyNodes, Joints, and degrees of freedom, managing their memory and lifecycle. MetaSkeleton is a pure abstract base class that defines the API for querying and controlling these components but owns nothing. Skeleton inherits from MetaSkeleton, allowing you to pass a Skeleton to any function expecting a MetaSkeleton reference or pointer.

When should I use ReferentialSkeleton instead of a full Skeleton?

Use ReferentialSkeleton when you need to operate on a subset of bodies (such as a single arm of a humanoid) or combine bodies from multiple Skeleton instances without copying the underlying model data. It is ideal for creating lightweight views for controllers, avoiding the memory overhead of duplicating complex robot models while maintaining full access to kinematic and dynamic queries.

How do I ensure thread safety when using MetaSkeleton across multiple threads?

Call getLockableReference() on your MetaSkeleton instance to obtain a LockableReference object. This reference provides mutex protection for the underlying data structures, ensuring that concurrent reads and writes to positions, velocities, or forces remain consistent. This mechanism is essential for multithreaded simulation environments where physics steps and controller updates run on separate threads.

Can I clone a MetaSkeleton to create an independent copy for optimization or prediction?

Yes. The MetaSkeleton interface declares cloneMetaSkeleton(), which produces an identical but independent copy of the skeleton structure and state. This is particularly useful for creating temporary "what-if" scenarios in trajectory optimization, predictive control, or parallel simulation runs where you need to explore possible future states without affecting the original model.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →